bashは16進数値0x00を変数に格納できません


11

私はddでいくつかのトリックをしようとしています。私はそれをddにパイプするために "header"と呼ばれる変数にいくつかの16進値を格納することは可能だと思いました。

変数を使用しない最初のステップは次のとおりです。

$ echo -ne "\x36\xc9\xda\x00\xb4" |dd of=hex
$ hd hex

00000000  36 c9 da 00 b4                                    |6....|
00000005

その後、私はこれを試しました:

$ header=$(echo -ne "\x36\xc9\xda\x00\xb4") 
$ echo -n $header | hd

00000000  36 c9 da b4                                       |6...|
00000004

ご覧のとおり\x00$header変数の値が失われました。誰かがこの動作の説明をしていますか?これは私を夢中にさせています。


わかりますbash: warning: command substitution: ignored null byte in input
Kusalananda

引用符がありませんheader="$(echo -ne "\x36\xc9\xda\x00\xb4")"; echo -n "$header" | hdが、同じ結果になるだけです。
ctrl-alt-delor 2017

これは機能header="\x36\xc9\xda\x00\xb4"; echo -n "$header" | hdしますが、人間が読める形式で保存しているのと同じではありません。
ctrl-alt-delor 2017

回答:


16

BashはCスタイルの文字列を使用するため、文字列にnullバイトを格納できません。これは、nバイトをターミネーター用に予約するCスタイルの文字列です。したがって、Bashがnullバイトを途中で保存する必要なく、nullバイトを含むシーケンスを単純にパイプ処理するようにスクリプトを書き直す必要があります。たとえば、これを行うことができます:

printf "\x36\xc9\xda\x00\xb4" | hd

ちなみに、あなたが必要としないことに注意してくださいechoprintfこのために他の多くの単純なタスクでBashを使用できます。

または、チェーンの代わりに、一時ファイルを使用できます。

printf "\x36\xc9\xda\x00\xb4" > /tmp/mysequence
hd /tmp/mysequence

もちろん、これにはファイル/tmp/mysequenceがすでに存在している可能性があるという問題があります。次に、一時ファイルを作成し、そのパスを文字列に保存し続ける必要があります。

または、プロセス置換を使用してそれを回避できます。

hd <(printf "\x36\xc9\xda\x00\xb4")

<(command)オペレータは、の出力を受け取るファイルシステムの名前付きパイプを作成しますcommandhd最初の引数として、そのパイプへのパスを受け取ります。これは、ほとんどのファイルと同じように開いて読み取ります。詳しくは、https//unix.stackexchange.com/a/17117/136742をご覧ください


1
これは正しいですが、これは実装の詳細であり、正確な理由ではありません。私はそれを見ました、そしてPOSIX標準は実際この振る舞いを要求するので、あなたは実際の理由があります。(一部の人が指摘したように、zshそれを実行しますが、nōn-POSIXモードでのみです。)これを実装する価値があるかどうか疑問に思っていたので、実際に調べましたmksh
mirabilos

@mirabilos、あなたはそれを拡張して気にかけますか?AFAICT、出力にNUL文字がある場合のコマンド置換のPOSIXごとの動作は指定されていません。POSIXモードのzshの場合、私が考えることができる唯一の関連する違いは、shエミュレーションで\0は、のデフォルト値にないということです$IFSecho "$(printf 'a\0b')"でのshエミュレーションでは引き続き問題なく動作しzshます。
ステファンChazelas

3
@mirabilosシェルがPOSIX標準より10年以上古いことを考えると、実際の理由は、シェルがCスタイルの文字列を使用していて、標準がその周りに構築されていたことがわかると思います。
ジュスティ

printfとechoの詳細な議論に適したQを見つけました。unix.stackexchange.com/questions/65803/...
Paulb

8

あなたは使用することができzsh、その変数にNUL文字を格納できるだけのシェルである代わりに。その文字は、さえのデフォルト値であることを起こる$IFSの中でzsh

nul=$'\0'

または:

nul=$'\x0'

または

nul=$'\u0000'

または

nul=$(printf '\0')

ただし、引数や環境変数は、システムコールに渡されるNUL区切りの文字列(シェルではなくシステムのAPIの制限)として実行されるコマンドに、引数や環境変数として渡すことはできません。execve())。ではzsh、あなたはしかし、NULが機能または組み込みコマンドの引数としてバイトを渡すことができます。

echo $'\0' # works
/bin/echo $'\0' # doesn't

1
「代わりにzshを使用できます」。結構です-私は今、初心者としてbashスクリプトを教えています。他の構文と混同したくありません。しかし、それを提案してくれて本当にありがとう
フランク

実際のところ、zsh質問では構文を使用しました。echo -n $header内容渡すことを意味する$headerために、最後の引数として変数をecho -nあるzsh(またはfishまたはrcまたはes)構文ではなく、bash 構文。ではbash、それは非常に異なる意味を持ってます。より一般的にzshkshbash、GNUシェル、多かれ少なかれの一部のクローンksh、Unixの事実上のシェル)に似ていますが、Bourneシェルの設計の特異性のほとんどが修正されています(そして多くの追加機能、そして多くのよりユーザーフレンドリー/驚くほど少ない)。
ステファンChazelas

注意してください:zshは時々ゼロバイトを変更するかもしれません:echo $(printf 'ab\0cd') | od -vAn -tx1c `61 62 20 63 64 0a`を出力します、それはNULが存在するはずのスペースです。
sorontar 2017

1
そして、それは他の(なし、nil)シェルが再現しないものです。これにより、zshではスクリプトが非常に特殊な方法で動作します。私の意見では、zshは賢すぎようとしています。
sorontar 2017

2
POSIX sh標準に存在するzshスクリプトの記述に慣れるという設計上の誤りを「修正」すると、他のシェルで実行するとバグのあるプラクティスに慣れることになります。これは、他の言語とは異なり、スキルや習慣が伝わりそうにないような構文の問題ではありませんが、実際にはそうではありません。
Charles Duffy
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.