Bashでnullバイトを使用するにはどうすればよいですか?


33

Bashのファイルパスにはヌルバイト(ゼロ値のバイト、$'\0')以外の任意の文字を含めることができるため、セパレータとしてヌルバイトを使用するのが最善であると読みました。たとえば、の出力findを別のプログラムに送信する場合、-print0オプションを使用することをお勧めします(findそのバージョンの場合)。

しかし、このようなものは正常に機能しますが(改行で区切られたファイルパスを印刷します。心配する必要はありません。これは単なるデモであり、実際のスクリプトでは実際に実行していません)。

find -print0 \
  | while IFS= read -r -d $'\0' ; do echo "$REPLY" ; done

このようなものは機能しませ

for file in * ; do echo -n "$file"$'\0' ; done \
  | while IFS= read -r -d $'\0' ; do echo "$REPLY" ; done

for-loop部分だけを試すと、nullバイトを間に入れずに、すべてのファイル名を一緒に印刷することがわかります。

どうしてこれなの?どうしたの?

回答:


43

Bashは内部的にCスタイルの文字列を使用し、ヌルバイトで終了します。これは、Bash文字列(変数の値、コマンドへの引数など)に実際にNULLバイトが含まれることがないことを意味します。たとえば、このミニスクリプト:

foobar=$'foo\0bar'    # foobar='foo' + null byte + 'bar'
echo "${#foobar}"     # print length of $foobar

実際に印刷し3ているため、$foobarただ実際には'foo'bar文字列の末尾の後に来ます。

同様に、パーツを知らないため、echo $'foo\0bar'単にprintします。fooecho\0bar

ご覧のように、\0シーケンスは実際には$'...'-style文字列では非常に誤解を招きます。文字列内ではヌルバイトのように見えますが、そのようには機能しません。最初の例では、readコマンドにはがあり-d $'\0'ます。これは機能しますが、それ-d ''も機能するからです!(これはの明示的に文書化された機能ではありませんreadが、同じ理由で機能すると仮定します:''空の文字列であるため、その終端のヌルバイトがすぐに来ます。「デリムの最初の文字」を使用して文書化され、 「最初の文字」が文字列の終わりを超えている場合!)-d delim

あなたから知っているとしてではなくfind例として、それはあるヌルバイトをプリントアウトするためのコマンドについて、および入力として、それを読み込み、別のコマンドにパイプされるそのバイトに対して可能。Bash内の文字列に nullバイトを格納することに依存する部分はありません。2番目の例の唯一の問題は$'\0'、コマンドの引数で使用できないことです。echo "$file"$'\0'あなたがそれを望んでいることがわかっている場合に限り、最後にnullバイトを喜んで印刷することができます。

したがって、を使用する代わりに、を使用してecho、-style文字列printfと同じ種類のエスケープシーケンスをサポートでき$'...'ます。そうすれば、文字列内にヌルバイトを入れなくても、ヌルバイトを印刷できます。次のようになります。

for file in * ; do printf '%s\0' "$file" ; done \
  | while IFS= read -r -d '' ; do echo "$REPLY" ; done

または単にこれ:

printf '%s\0' * \
  | while IFS= read -r -d '' ; do echo "$REPLY" ; done

(注:echo実際には、nullバイトを-e処理\0して印刷できるフラグもありますが、ファイル名に含まれる特別なシーケンスを処理しようとするため、このprintfアプローチはより堅牢です。)


尚、一部のシェルがあります nullは内部の文字列をバイト許します。たとえば、Zshでの例は正常に機能します(デフォルト設定を前提としています)。ただし、シェルに関係なく、Unixライクなオペレーティングシステムは、プログラムへの引数にヌルバイトを含める方法を提供しません(プログラムの引数はCスタイルの文字列として渡されるため)。常にいくつかの制限があります。(あなたの例では、という理由だけでZshの中で作業することができますechoZshのは、他のプログラムを呼び出すためのOSのサポートに頼ることなく、それを呼び出すことができますので。あなたが使用している場合、シェル組み込みであるcommand echo代わりにしecho、それは組み込みをバイパスし、スタンドアロンで使用ようechoにプログラムを$PATH、 ZshでBashと同じ動作が見られます。)


2
-d ''すでに区切りを意味する場合、IFSが何も設定されないのはなぜ\0ですか?ここで説明を見つけました:stackoverflow.com/questions/8677546/…–
CMCDragonkai
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.