文字列のエンコードとデコード
printf '%s' 'héllo wörld' | base64 # aMOpbGxvIHfDtnJsZA==
printf '%s' 'aMOpbGxvIHfDtnJsZA==' | base64 -d # héllo wörld
base64 はバイト列をエンコードするので、結果は端末の文字コードに従います。最近の Linux や macOS なら UTF-8 です。
よくある失敗が echo で、末尾に改行が付きます。
echo 'héllo wörld' | base64 # aMOpbGxvIHfDtnJsZAo= (note the "o=")
echo -n hi | base64 # aGk=
echo -n は Bash では動きますが、すべての sh で動くわけではありません。printf '%s' ならどこでも使えます。ヒアストリング(base64 <<< "text")も改行を付けます。受け取った Base64 が Cg== や o= で終わっていたら、末尾に改行が含まれている可能性が高いです。Base64 デコーダー に貼り付けて確かめてみてください。
GNU(Linux)と BSD(macOS)のフラグの違い
| 操作 | GNU coreutils(Linux) | macOS |
|---|---|---|
| デコード | -d、--decode | -D、--decode(最近のバージョンは -d も可) |
| 折り返しなし | -w 0 | デフォルト |
| N 文字で折り返し | -w N(デフォルト 76) | -b N |
| 入力ファイル | base64 file | base64 -i file または base64 < file |
| 出力ファイル | > でリダイレクト | -o file またはリダイレクト |
| デコード時に不要な文字を無視 | -i、--ignore-garbage | なし |
2 つの -i は意味が違います。macOS では入力ファイルの指定、Linux では不要な文字の無視です。移植性のあるスクリプトでは、標準入力から読み、標準出力に書くようにしてください。
base64 < in.bin | tr -d '\n' > out.b64 # same output on Linux and macOS
base64 --decode < out.b64 > in-copy.bin
GNU の base64 はデフォルトで 76 文字で折り返すため、ヘッダーや環境変数が壊れます。
head -c 60 /dev/zero | tr '\0' x | base64 -w 0
# eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
-w 0 を付けないと、1 行目が 76 文字になり、残りは 2 行目に送られます。MIME 形式の行 が必要な場合は、デフォルトの折り返しがちょうど求める形です。
URLセーフな Base64
GNU coreutils 8.31 以降には、Base64URL に対応した basenc が含まれています。パディングは残ります。
printf '%s' '<<???>>' | basenc --base64url # PDw_Pz8-Pg==
printf '%s' 'PDw_Pz8-Pg' | basenc --base64url -d # <<???>>
macOS や古い Linux では tr で置き換えます。
printf '%s' '<<???>>' | base64 | tr '+/' '-_' | tr -d '=' # PDw_Pz8-Pg
デコードには逆の置き換えが必要で、厳格なデコーダー向けにはパディングも戻します。
s=PDw_Pz8-Pg
s=$(printf '%s' "$s" | tr -- '-_' '+/')
while [ $(( ${#s} % 4 )) -ne 0 ]; do s="$s="; done
printf '%s' "$s" | base64 -d # <<???>>
-- は、tr が -_ をオプションとして解釈するのを防ぎます。JWT に必要なのも同じ変換で、JWT デコーダー なら自動で処理してくれます。詳しくは Base64 と Base64URL の違い をご覧ください。
ファイルのエンコードとデコード
base64 -w 0 hello.txt # aGVsbG8K
base64 -d <<< 'aGVsbG8K' > copy.txt
base64 -d payload.b64 > payload.pdf
base64 はストリーム処理でブロック単位に読み書きするので、ファイルサイズは問題になりません。画像から Data URI を作るには次のようにします。
printf 'data:image/png;base64,%s' "$(base64 -w 0 logo.png)"
HTTP の Basic 認証ヘッダーなら次のとおりです。
printf 'Basic %s' "$(printf '%s' 'user:pass' | base64)" # Basic dXNlcjpwYXNz
Basic Auth ヘッダージェネレーター を使えば、パスワード入りのコマンドがシェルの履歴に残ることなく同じ結果が得られます。
検証、エラー、終了コード
GNU の base64 -d は不正な文字を見つけると、標準エラー出力に base64: invalid input を表示し、ステータス 1 で終了します。それでも、そこまでにデコードした内容はすべて書き出すので、デコードに失敗すると中途半端なファイルが残ることがあります。
printf 'aGk=!!' | base64 -d # prints "hi", then: base64: invalid input
echo $? # 1 (without a pipe after it)
printf 'aGk=!!' | base64 -d -i # hi (garbage ignored, exit 0)
coreutils 9.7 での結果:
| 入力 | 結果 |
|---|---|
aGk | hi、終了コード 0(パディング不足は許容) |
"aGVs\nbG8K" | hello、終了コード 0(改行は無視) |
aGl= | hi の後に invalid input、終了コード 1 |
a | invalid input、終了コード 1 |
PDw_Pz8-Pg== | 一部だけ出力、invalid input、終了コード 1 |
検証には終了ステータスを使い、失敗しても正常なデータを上書きしないよう、一時ファイルにデコードしてください。
if printf '%s' "$b64" | base64 -d > /tmp/out.bin 2>/dev/null; then
mv /tmp/out.bin result.bin
else
echo "invalid Base64" >&2
fi
パイプラインでは、$? は最後のコマンドのステータスです。base64 -d が最後でない場合は set -o pipefail を追加してください。ログやメールからコピーした文字列がデコードできないときは、Base64 の修復 が引用符、空白、接頭辞を取り除きます。それぞれの失敗の原因は エラーの解説 で説明しています。
その他のコマンドラインツール
openssl base64はほぼすべてのシステムにあり、Linux と macOS で同じように動作します。OpenSSL での Base64 をご覧ください。- coreutils がない環境でも
python3 -m base64 -d fileとpython3 -m base64 fileが使えます。出力は 76 文字で折り返されます。 - Kubernetes では
kubectl get secret x -o jsonpath='{.data.password}' | base64 -dがお決まりのパターンです。Kubernetes Secret デコーダー も使えます。
よくある質問
echo hello | base64 の結果がオンラインツールと違うのはなぜですか?
echo は改行を付け加えるため、hello(aGVsbG8=)ではなく hello\n(aGVsbG8K)をエンコードしてしまいます。printf '%s' hello か echo -n hello を使ってください。
base64 の出力が折り返されないようにするには?
Linux では base64 -w 0 を使います。macOS はデフォルトで折り返しません。どちらでも同じ動作にしたいなら、tr -d '\n' にパイプしてください。
「base64: invalid input」とはどういう意味ですか?
GNU の base64 -d が、アルファベット外の文字か壊れたパディングに遭遇したということです。そこまでにデコードしたバイト列を出力した後でエラーを表示し、終了ステータス 1 で終了します。よくある原因は、URLセーフの - と _、引用符、data: 接頭辞です。
macOS の base64 -d は Linux と同じですか?
古い macOS は -D(または --decode)しか認識しませんでしたが、最近のバージョンは -d も受け付けます。両方で動かすスクリプトでは --decode を使ってください。