$ FILE="$(mktemp)"
$ printf "a\0\n" > "$FILE"
$ od -tx1z "$FILE"
0000000 61 00 0a >a..<
0000003
今まではそんなに良くなった。
上記の内容をbashスクリプトでカプセル化しました。
#! /bin/bash
cmd=("$@")
FILE="$(mktemp)"
eval "${cmd[@]}" > "$FILE"
od -tx1z "$FILE"
しかし、
$ script printf 'a\0\n'
0000000 61 30 6e >a0n<
0000003
出力が\0
リテラル文字列になるのはなぜですか?これが起こらないようにするにはどうすればよいですか?
この投稿ではあまり重要ではありません。私の問題は、いくつかのコマンドをbashスクリプトでラップしようとした結果です。NUL 除去によるコマンド拡張の防止:
FILE="$(mktemp)"
printf "a\0\n" > "$FILE"
S="$(uuencode -m "$FILE" /dev/stdout)"
uudecode -o /dev/stdout <(printf "$S") | od -tx1
rm "$FILE"
答え1
出力が
\0
リテラル文字列になるのはなぜですか?これが起こらないようにするにはどうすればよいですか?
.eval
バックスラッシュでエスケープされた 0 と 0 を使用してeval
コマンドを実行し、バックスラッシュを削除してそのままにします。printf a\0\n
n
代わりに、これを効果的に防ぐことができますeval
。を使用すると、"$@" > "$FILE"
スクリプト引数で指定されたコマンドを実行できる必要があります。ただし、この場合は使用できませんeval
。あるいは、コマンドを引数として渡す必要がないように全体を再設計することもできます。
コマンド拡張がNULを削除するのを防ぐために、いくつかのコマンドをbashスクリプトでラップしようとしています。
S="$(uuencode -m "$FILE" /dev/stdout)"
これはここで問題ですか?uuencode -m
NULバイトを生成してはいけません。バイナリデータをテキストにエンコードするので正反対です。
最後のスクリプトはa
、NUL および改行文字を作成して に渡します。これにより、16進表現または同様の内容が$FILE
印刷されます。od
0000000 61 00 0a
答え2
NULをstdout、stderrとして出力するか、stdinから受け取ることができます。
何でも「内部」捕獲そして(ほぼ)NULを使うと問題になります。
stdoutにNULを発行できます。
$ printf 'a\0b' | sed -n l
a\000b$
または:
$ printf 'a\0b' | cat -A
a^@b
ファイルもあります:
$ printf 'a\0b' >outfile
そして正確にこれはスクリプトにも当てはまります。
評価する
最初のスクリプトの問題は、次を使用していることですeval
。
$ cmd=(printf 'a\0b\n')
$ echo "${cmd[@]}"
printf a\0b\n
$ eval echo "${cmd[@]}"
printf a0bn
2番目のループでは、バックスラッシュを解析するシェル行が削除されます。
体験版を使用しないでください。
$ "${cmd[@]}"| sed -n l
a\000b$
拡大する
これはprintf
、組み込みタスクとstdout
NUL操作の両方が可能であることを証明します。
しかし、これは失敗します。
$ printf '%s\n' "$(printf 'a\0b')" | cat -A
bash: warning: command substitution: ignored null byte in input
ab$
(bash 4.4+では)警告メッセージもあります。
つまり、シェル(ほとんどのシェル)は内部的にC文字列を使用し、C文字列は最初のNULで終了します。一部のシェルは、「コマンド拡張」の最初のNULで切り捨てられます。
$ ksh -c 'echo $(printf "a\0b\n")|sed -n l' # also yash
a$
一部はNULを削除しました。
$ bash -c 'echo $(printf "a\0b\n")|sed -n l'
ab$
一部はNULを空白に変更することもあります。
$ zsh -c 'echo $(printf "a\0b\n")|sed -n l'
a b$
変数の割り当てでも同様の問題が発生します。
コーディング
はい、リンクされた回答はuuencode
ファイルの内容をエンコード(およびデコード)します。
より簡単なアプローチは次のとおりですxxd
(8進ダンプを元に戻すことができます)。
FILE="$(mktemp)"
printf "a\0b\n" > "$FILE"
S=$(xxd -p "$FILE")
xxd -r -p <(printf '%s' "$S") | xxd -p
rm "$FILE"