36

区切り文字としてNULL文字を使用してファイル/フォルダー名のリストを正しく処理しようとしているときに(他の質問を参照)、理解できないBashの奇妙な動作に遭遇しました。

1つ以上のNULL文字を含む文字列を変数に割り当てると、NULL文字は失われる/無視される/保存されません。

例えば、

echo -ne "n\0m\0k" | od -c   # -> 0000000   n  \0   m  \0   k

だが:

VAR1=`echo -ne "n\0m\0k"`
echo -ne "$VAR1" | od -c   # -> 0000000   n   m   k

これは、その文字列をファイル(たとえば、/ tmp)に書き込み、直接配管することが望ましくない、または実行可能でない場合は、そこから読み取る必要があることを意味します。

これらのスクリプトをZシェル(zsh)で実行すると、どちらの場合も\ 0を含む文字列が保持されますが、残念ながら、Bashが存在するはずのスクリプトを実行しているシステムにzshが存在するとは限りません。

\ 0文字を含む文字列を(メタ)文字を失うことなく効率的に保存または処理するにはどうすればよいですか?

4

4 に答える 4

39

Bashでは、NULL文字を変数に格納することはできません。

ただし、コマンドを使用して、データのプレーンな16進ダンプを保存する(後でこの操作を再度逆にする)ことはできxxdます。

VAR1=`echo -ne "n\0m\0k" | xxd -p | tr -d '\n'`
echo -ne "$VAR1" | xxd -r -p | od -c   # -> 0000000    n  \0   m  \0   k
于 2011-07-04T12:32:22.710 に答える
20

他の人がすでに述べているように、NUL charを保存/使用することはできません

  • 変数で
  • コマンドラインの引数で。

ただし、任意のバイナリデータ(NUL文字を含む)を処理できます。

  • パイプで
  • ファイル内

だからあなたの最後の質問に答えるために:

\ 0文字を含む文字列を(メタ)文字を失うことなく効率的に格納または処理する方法について、誰かにヒントを教えてもらえますか?

ファイルまたはパイプを使用して、任意のメタ文字を含む任意の文字列を効率的に格納および処理できます。

データを処理する場合は、さらに次の点に注意する必要があります。

制限を回避する

変数を使用する場合は、NUL charをエンコードして削除する必要があります。ここでの他のさまざまなソリューションは、それを行うための賢い方法を提供します(明らかな方法は、たとえばbase64エンコード/デコードを使用することです)。

メモリや速度が気になる場合は、最小限のパーサーを使用して、NUL文字(および引用符で囲まれた文字)のみを引用することをお勧めします。この場合、これはあなたを助けるでしょう:

quote() { sed 's/\\/\\\\/g;s/\x0/\\x00/g'; }

次に、機密データをにパイプすることにより、変数とコマンドライン引数にデータを格納する前にデータを保護できます。quoteこれにより、NUL文字のない安全なデータストリームが出力されます。これを使用すると、元の文字列(NUL文字を含む)を元に戻すことができますecho -en "$var_quoted"。これを使用すると、標準出力で正しい文字列が送信されます。

例:

## Our example output generator, with NUL chars
ascii_table() { echo -en "$(echo '\'0{0..3}{0..7}{0..7} | tr -d " ")"; }
## store
myvar_quoted=$(ascii_table | quote)
## use
echo -en "$myvar_quoted"

注:| hd16進数でデータをきれいに表示し、NUL文字が失われていないことを確認するために使用します。

ツールの変更

コマンドラインで変数や引数を使用せずにパイプを使用できることを忘れないでください。たとえば、<(command ...)名前付きパイプ(一時ファイルの一種)を作成する構造を忘れないでください。

編集:の最初の実装は正しくなく、によって解釈される特殊文字quoteを正しく処理しませんでした。それを見つけてくれてありがとう@xhienne。\echo -en

EDIT2:の2番目の実装には、実際にはより多くのゼロを、、、と同等に消費するよりも多くを使用するためquoteにバグがありました。したがって、に置き換えられました。これを見つけてくれた@MatthijsSteenに感謝します。\0\0\00\000\0000\0\x00

于 2014-07-01T13:41:41.847 に答える
12

POSIXの移植性のuuencodeためにuudecode

xxdPOSIX 7ではありbase64 ませんが、uuencodeはです。

VAR="$(uuencode -m <(printf "a\0\n") /dev/stdout)"
uudecode -o /dev/stdout <(printf "$VAR") | od -tx1

出力:

0000000 61 00 0a
0000003

残念ながら、<()ファイルへの書き込みを除いて、Bashプロセス置換拡張機能のPOSIX 7の代替手段は見当たりません。また、デフォルトではUbuntu 12.04(sharutilsパッケージ)にインストールされていません。

したがって、本当の答えは次のとおりだと思います。これにはBashを使用せず、Pythonまたはその他の賢明なインタプリタ言語を使用してください。

于 2014-04-10T10:28:35.233 に答える
3

私はジェフの答えが大好きです。xxdの代わりにBase64エンコーディングを使用します。それは少しスペースを節約し、(私が思うに)何が意図されているかについてより認識しやすくなるでしょう。

VAR=$(echo -ne "foo\0bar" | base64)
echo -n "$VAR" | base64 -d | xargs -0 ...

-eに関しては、エンコードされたnull('\ 0')を持つリテラル文字列のエコーに必要ですが、ユーザー入力を次のようにエコーしている場合、「echo-e」が安全でないことについても思い出すようです。エコーが解釈して悪い結果をもたらすエスケープシーケンスを挿入する可能性があります。エンコードされた格納された文字列をデコードにエコーする場合、-eフラグは必要ありません。

于 2011-07-19T15:33:12.163 に答える