Postfixのキューやバックアップした .eml ファイルからメールの件名を取り出そうとしたとき、=?UTF-8?B?44GT44KT44Gr44Gh44Gv?= のような文字列に直面することがあります。これはRFC 2047に基づくMIMEエンコード済みワードと呼ばれる形式で、メールヘッダーに日本語などのマルチバイト文字を安全に格納するための仕組みです。そのままシェルスクリプトで処理すると文字化けや条件分岐の誤動作につながります。
デコードの最短手段は nkf -m -w コマンドです。UTF-8・ISO-2022-JP のどちらの形式も自動判別して復号できます。nkf が使えない環境ではPython3の標準ライブラリで同等の処理が可能です。
MIMEエンコードの構造を最低限理解する
MIMEエンコード済みワードは =?文字セット?エンコード方式?エンコード済み文字列?= という形式です。エンコード方式は B(Base64)と Q(Quoted-Printable)の2種類があり、日本語メールでは B が大多数を占めます。
# UTF-8 + Base64(現在の主流)
=?UTF-8?B?44GT44KT44Gr44Gh44Gv?=
# ISO-2022-JP + Base64(古いシステム・メーラー)
=?ISO-2022-JP?B?GyRCJSglcyUzITwlSRsoQg==?=
# Quoted-Printable の例
=?UTF-8?Q?=E3=81=93=E3=82=93=E3=81=AB=E3=81=A1=E3=81=AF?=
長い日本語件名では1行の制限(76文字)を超えるため、複数のエンコード済みワードに分割されることがよくあります。nkf -m や Python3 の email.header モジュールはこの連結を自動で処理します。
nkfでMIMEデコードする
nkf は主要なディストリビューションでパッケージが提供されています。
# Ubuntu / Debian
sudo apt install nkf
# RHEL 9 / Rocky Linux / AlmaLinux(EPELが必要)
sudo dnf install epel-release && sudo dnf install nkf
-m でMIMEデコード、-w で出力をUTF-8に揃えます。文字セットはエンコード済みワードの宣言から自動で判断されます。
# ISO-2022-JP 形式をデコード
echo '=?ISO-2022-JP?B?GyRCJSglcyUzITwlSRsoQg==?=' | nkf -m -w
# 出力: エンコード
# UTF-8 形式をデコード
echo '=?UTF-8?B?44GT44KT44Gr44Gh44Gv?=' | nkf -m -w
# 出力: こんにちは
# ファイルをまとめてデコード
nkf -m -w encoded.txt > decoded.txt
nkfでMIMEエンコードする
シェルスクリプトからメールを送信するとき、日本語の件名を手動でエンコードしなければならない場面があります。-M でMIMEエンコード、-B でBase64方式を指定します。
# UTF-8 テキストを ISO-2022-JP の MIME エンコードに変換
echo 'テスト件名' | nkf -Mj
# 出力例: =?ISO-2022-JP?B?GyRCJTM...?=
# UTF-8 MIME エンコード(モダンなメーラー向け)
echo 'テスト件名' | nkf -Mw
# 出力例: =?UTF-8?B?44OG44K...?=
注意: sendmail や postfix の sendmail コマンドで Subject: ヘッダーを直接渡す場合、エンコードは自動では行われません。スクリプト経由で送信するときは事前にエンコード済みの文字列を生成してヘッダーに埋め込むか、Python の smtplib + email ライブラリのようにMIME対応の送信手段を使うのが確実です。
Python3でMIMEデコードする(移植性重視の環境向け)
コンテナや最小構成サーバーでは nkf を追加インストールしにくいことがあります。Python3 の標準ライブラリだけで同等の復号が可能です。
# ワンライナーでMIMEデコード
python3 -c "
import sys, email.header
raw = sys.stdin.read().strip()
parts = email.header.decode_header(raw)
print(''.join(
p.decode(enc or 'utf-8') if isinstance(p, bytes) else p
for p, enc in parts
))
" <<< '=?ISO-2022-JP?B?GyRCJSglcyUzITwlSRsoQg==?='
# 出力: エンコード
スクリプト内で繰り返し使う場合は関数化しておくと可読性が上がります。
#!/usr/bin/env python3
import email.header
def decode_mime_header(raw: str) -> str:
parts = email.header.decode_header(raw)
return ''.join(
p.decode(enc or 'utf-8') if isinstance(p, bytes) else p
for p, enc in parts
)
print(decode_mime_header('=?UTF-8?B?44GT44KT44Gr44Gh44Gv?='))
# こんにちは
実務での使い方:.emlファイルから件名を抽出・復号する
ある運用現場では、メールサーバーのバックアップから .eml ファイルを取り出し、件名でフィルタリングするスクリプトを定期的に走らせています。以下はその典型的なパイプラインです。
# .eml から Subject ヘッダーを取り出してデコード(nkf)
grep -i '^Subject:' /var/mail/sample.eml \
| head -1 \
| sed 's/^[Ss]ubject: //' \
| nkf -m -w
# Python3 で .eml を丸ごとパースして件名取得(推奨)
python3 - < sample.eml <<'EOF'
import sys, email, email.header
msg = email.message_from_file(sys.stdin)
subject = email.header.make_header(email.header.decode_header(msg['Subject']))
print(str(subject))
EOF
Postfixのキューに溜まったメールを確認するには postcat と組み合わせます。
# Postfix キューの件名を確認(メールIDは postqueue -p で確認)
sudo postcat -q <メールID> \
| grep -i '^subject:' \
| head -1 \
| sed 's/^[Ss]ubject: //' \
| nkf -m -w
よくある落とし穴と対処
ヘッダーが途中で切れる: RFC 2047では1行の長さに制限があるため、長い件名は複数のエンコード済みワードに分割され、次行がスペースまたはタブで始まる形(折り返し)になります。grep で1行だけ取り出すと後半が欠落します。Python3の email.message_from_file() はこの折り返しを自動で処理するため、複数行にまたがる件名でも正確に取得できます。
デコード後も文字化けする: ヘッダーに宣言された文字セット(例: ISO-2022-JP)と実際のバイト列が一致していないことがまれにあります。nkf --guess で推測してから変換を試みてください。
# Base64 部分を手動デコードして実際の文字コードを調べる
echo 'GyRCJSglcyUzITwlSRsoQg==' | base64 -d | nkf --guess
nkf のバージョン差: -M オプションの動作は nkf 2.1.5 未満と以降で挙動が異なる場合があります。nkf --version で確認し、古いバージョンが入っているディストリ(CentOS 7系など)では明示的に -Mj・-Mw のように文字セットを指定してください。エンコード処理を本番スクリプトに組み込む場合は、Python3 の email ライブラリを使う方が環境依存を避けられます。
「コマンドは打てる。次は"現場で通用する型"を最短で身につけたい」——そんなあなたへ。
ネットの断片的なTipsをコピペするだけでは、体系的な運用スキルはなかなか積み上がりません。リナックスマスター.JPの無料メルマガ「リナマガ」では、現場で実際に使うLinuxサーバー運用の考え方を、順を追って実務目線でお届けしています。いま登録された方には、安全なサーバー構築の「型」をまとめた『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼント中です。
>> 図解60P『Linux入門マニュアル』を無料で受け取る
(メルマガ登録は10秒・いつでも解除OK)
※ 「独学の時間がもったいない」「プロから現場の技術を最短で学びたい」という本気の方には、2日で実務レベルが身につく初心者向けハンズオンセミナーもご用意しています。
