1

エグゼクティブサマリー

プロセス置換を行うときにシェルが NUL バイトをスキップするのは標準的な動作ですか?

たとえば、

printf '\0abc' | read value && echo $value

お譲り致しabcます。printf出力の 16 進ダンプで明らかに出力されていることが示されていても、NUL 値はスキップされます。

私の最初の考えは「単語分割」でした。ただし、実際のプロセス代替を使用する場合は、

value=$(printf '\0abc')

結果は類似しており、=単語分割は実行されません。

長い話

この質問に対する適切な答えを探しているときに、プロセス置換から変数に値を読み取るときに、私がかなり慣れている少なくとも 3 つのシェル実装 (ash、zsh、および bash) が NUL 文字を無視することに気付きました。

これが発生するパイプラインの正確なポイントは異なるようですが、最初から存在しなかったかのように、一貫して NUL バイトがドロップされます。

いくつかの実装を確認しましたが、これは正常な動作のようです。

ashinputスキップし'\0'ますが、これが純粋な偶然なのか意図した動作なのかは、コードからは明らかではありません。

if (lastc != '\0') {
    [...]
}

bashソース コードには、プロセス置換で NUL 値をスキップしたことを示す明示的な#ifdef警告が含まれています。

#if 0
      internal_warning ("read_comsub: ignored null byte in input");
#endif

zshさんの行動についてはよくわかりません。(内部関数'\0'で定義されているように) メタ文字として認識し、特殊なサロゲート文字を先頭に追加し、入力文字にビット #5 を設定して、基本的にメタ文字を解除します。これにより、makeもスペースになります) 。imeta()Meta'\0'' '

if (imeta(c)) {
    *ptr++ = Meta;
    c ^= 32;
    cnt++;
}

value上記のprintfコマンドにメタ文字が含まれているという証拠がないため、これは後で削除されるようです。私はzshの内部構造に慣れていないので、これを大いに参考にしてください。また、副作用のないステートメントにも注意してください。

zshに NUL (メタエスケープ) を含めることもできることに注意してください(たとえば、なしIFSで単語を分割することが可能になります)。したがって、 との値に応じて異なる結果が得られるはずです(フィールド分割を行います)。find -print0xargs -0printf '\0abc' | read valuevalue=$(printf '\0abc')IFSread

4

1 に答える 1

4

現存するすべての POSIX シェルは、Pascal 文字列ではなく、C 文字列 (NUL で終了) を使用します (それらの長さは別のメタデータとして保持されるため、NUL を含めることができます)。したがって、文字列の内容に NUL を含めることはできません。これは特に Bourne Shell と ksh に当てはまり、どちらも POSIX sh 標準に大きな影響を与えました。

この仕様では、ここでシェルが実装定義の方法で動作することを許可しています。特定のシェルとリリースがターゲットになっていることを知らなければ、最初の NUL で返されたストリームを終了してから単に NUL を完全に破棄するまでの間の特定の動作は期待できません。引用

シェルは、サブシェル環境 (シェル実行環境を参照) でコマンドを実行し、コマンド置換 (コマンドのテキストとそれを囲む "$()" または逆引用符) をコマンドの標準出力に置き換えることにより、コマンド置換を展開し、置換の最後にある 1 つ以上の文字のシーケンス。出力の終わりの前に埋め込まれた文字は削除されません。ただし、IFS の値と有効な引用符によっては、フィールド区切り文字として扱われ、フィールド分割中に削除される場合があります。出力に null バイトが含まれている場合、動作は規定されていません。


これは、広く利用可能なシェルで NUL を含むストリームを読み取ったり生成したりできないと言っているのではありません。プロセス置換を使用して、以下を参照してください (bash 用に記述されていますが、マイナーな変更があれば ksh または zsh で動作するはずです)。

# read content from stdin into array variable and a scalar variable "suffix"
array=( )
while IFS= read -r -d '' line; do
  array+=( "$line" )
done < <(process that generates NUL stream here)
suffix=$line # content after last NUL, if any

# emit recorded content
printf '%s\0' "${array[@]}"; printf '%s' "$suffix"
于 2015-09-22T16:50:42.333 に答える