なぜ$ '\ 0'は ''と同じですか?


10

いくつかのファイルで処理を行う一般的な方法は次のとおりです。そのために私を襲わないでください。

for f in $(ls); do 

ここで、スペースやその他の奇妙な文字を含むファイルに対して安全にするために、素朴な方法は次のようにすることです:

find . -type f -print0 | while IFS= read -r -d '' file; 

ここでは、-d ''はASCII NULをのように設定するための略語-d $'\0'です。

しかし、それはなぜですか?なぜ''$'\0'同じ?これは、空の文字列が常にnullで終了しているBashのCルートによるものですか?


「ナイーブ」な方法を参照して、これを行うより良い方法はありますか?
iruvar 2013年

2
ちなみに、一連のファイルを繰り返し処理して安全な操作を実行する場合for f in *は、解析の代わりに使用してくださいls

@htor私for i in $(ls)がひどく愚かであることを知っています—私はここで悪い例としてそれを使用したことをほとんど恥ずかしく思っています。
slhck 2013年

@ChandraRavooriはい、たとえば、find … -execファイルをループする代わりにを使用します。これは、代わりにそのようなforループを使用するほとんどの場合に機能します。ここでfindは、あなたのためにすべてを世話します。
slhck 2013年

@slhck、ありがとう。読みやすさの理由からループが望ましい場合がある各ファイルのマルチステップ操作が関係する状況についてはどうですか?上記の「単純な方法」よりも優れたループオプションはありますか?
iruvar 2013年

回答:


10

man page of bash読み取ります。

          -d delim
                 The first character of delim is  used  to  terminate  the
                 input line, rather than newline.

文字列は通常nullで終了するため、空の文字列の最初の文字はnullバイトです。- 私には理にかなっています。:)

ソースはこう読む:

static unsigned char delim;
[...]
    case 'd':
      delim = *list_optarg;
      break;

空の文字列delimは単にnullバイトです。


あなたは「文字列は通常nullで終了している」と言うとき、それはありません POSIX環境でのケースのどこか?私が学校でCを学んでいた頃から、もちろんそう考えるのは理にかなっています。ただ調べていたところです。
slhck 2013年

しかし、任意の文字列を任意の数の空の文字列を含むと見なすことができます。たとえば、 ''と "X"を連結すると、 "X"になります。したがって、bashが最初に遭遇するのは空の文字列であると主張できます。たとえば、JavaScriptで空の文字列を使用すると、文字split()ごとに分割されます。「歴史的な理由から」が私たちが得ることができる最良の説明かもしれないと思います。
donothings成功

まあ、それは正しくありません。Cスタイル'\0'を「連結」すると、これは、JavaScript @donなどの言語の高レベル関数とはあまり関係がありません'X\0''X\0'
slhck

ソースを追加してくれてありがとう、michas。delim = *list_optarg;なぜそうなのかを明らかにします。
slhck 2013年

@slhck:すみません、はっきりさせていません。あなたが「なぜ尋ねた''$'\0'同じ?」、michasは「それはコードが何をするかだ」の近接、について説明を行いました。空の文字列を処理する別の方法を概説しましたが、どちらか一方を選択することは、慣習または偶然の問題にすぎないことを示唆しています。
donothings成功

6

bashには、互いに補う2つの欠点があります。

を記述すると$'\0'、内部的には空の文字列と同じように扱われます。例えば:

$ a=$'\0'; echo ${#a}
0

これは、内部的にbashがすべての文字列をnullで終了するC文字列として格納するためです。null バイトは文字列の終わりを示します。bashは、文字列を最初のnullバイト(文字列の一部ではない!)に黙って切り捨てます。

# a=$'foo\0bar'; echo "$a"; echo ${#a}
foo
3

組み込みの-dオプションに文字列を引数として渡すとread、bashは文字列の最初のバイトのみを調べます。ただし、文字列が空でないことは実際にはチェックされません。内部的には、空の文字列は、nullバイトのみを含む1要素のバイト配列として表されます。したがって、bashは文字列の最初のバイトを読み取る代わりに、このnullバイトを読み取ります。

次に、内部的に、read組み込みの背後にある機構はnullバイトでうまく機能します。区切り文字が見つかるまで、1バイトずつ読み取り続けます。

他のシェルは異なる動作をします。たとえば、ashとkshは、入力を読み取るときにnullバイトを無視します。kshではksh -d ""、改行まで読み取ります。シェルは、バイナリデータではなくテキストにうまく対応できるように設計されています。Zshは例外です。nullバイトを含む任意のバイトに対応する文字列表現を使用します。zshでは、$'\0'長さ1の文字列です(ただしread -d ''、奇妙なことに、のように動作しますread -d $'\0')。


の動作がreadbash 4.3 で変更され、nullバイトをスキップするようになりました。たとえばの代わりににread x< <(printf a\\0a)設定xaaますa
Lri
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.