これは、要求された1行のソリューションです(「プロセス置換」を持つ最近のシェルの場合):
grep -o "ef be ad de" <(hexdump -v -e '/1 "%02x "' infile.bin) | wc -l
「プロセス置換」<(…)
が利用できない場合は、grepをフィルターとして使用します。
hexdump -v -e '/1 "%02x "' infile.bin | grep -o "ef be ad de" | wc -l
以下は、ソリューションの各部分の詳細な説明です。
16進数のバイト値:
最初の問題は簡単に解決できます。
これらの\ Xnnエスケープシーケンスは、魚の殻でのみ機能します。
上部X
を下部に変更し、x
printfを使用します(ほとんどのシェルの場合):
$ printf -- '\xef\xbe\xad\xde'
または使用:
$ /usr/bin/printf -- '\xef\xbe\xad\xde'
'\ x'表現を実装しないことを選択したシェルの場合。
もちろん、16進数を8進数に変換すると、(ほとんど)すべてのシェルで機能します。
$ "$sh" -c 'printf '\''%b'\'' "$(printf '\''\\0%o'\'' $((0xef)) $((0xbe)) $((0xad)) $((0xde)) )"'
ここで、「$ sh」は(合理的な)シェルです。しかし、それを正しく引用することは非常に困難です。
バイナリファイル。
最も堅牢な解決策は、ファイルとバイトシーケンス(両方)を(new line)0x0A
や(null byte)などの奇数の文字値に問題のないエンコーディングに変換することです0x00
。「テキストファイル」を処理するように設計および適合されたツールを使用して、両方を正しく管理することは非常に困難です。
base64のような変換は有効なように見えるかもしれませんが、mod 24(ビット)位置の1番目、2番目、または3番目のバイトであるかどうかによって、すべての入力バイトが最大3つの出力表現を持つ可能性があるという問題があります。
$ echo "abc" | base64
YWJjCg==
$ echo "-abc" | base64
LWFiYwo=
$ echo "--abc" | base64
LS1hYmMK
$ echo "---abc" | base64 # Note that YWJj repeats.
LS0tYWJjCg==
六角変換。
そのため、最も堅牢な変換は、単純なHEX表現のように、各バイト境界で始まる変換でなければなりません。
次のいずれかのツールを使用して、ファイルの16進表現でファイルを取得できます。
$ od -vAn -tx1 infile.bin | tr -d '\n' > infile.hex
$ hexdump -v -e '/1 "%02x "' infile.bin > infile.hex
$ xxd -c1 -p infile.bin | tr '\n' ' ' > infile.hex
この場合、検索するバイトシーケンスはすでに16進数になっています。
:
$ var="ef be ad de"
しかし、それは変容する可能性もあります。往復hex-bin-hexの例を次に示します。
$ echo "ef be ad de" | xxd -p -r | od -vAn -tx1
ef be ad de
検索文字列は、バイナリ表現から設定できます。上記のod、hexdump、またはxxdの3つのオプションはいずれも同等です。スペースが含まれていることを確認して、バイト境界で一致するようにします(ニブルシフトは許可されません)。
$ a="$(printf "\xef\xbe\xad\xde" | hexdump -v -e '/1 "%02x "')"
$ echo "$a"
ef be ad de
バイナリファイルが次のようになっている場合:
$ cat infile.bin | xxd
00000000: 5468 6973 2069 7320 efbe adde 2061 2074 This is .... a t
00000010: 6573 7420 0aef bead de0a 6f66 2069 6e70 est ......of inp
00000020: 7574 200a dead beef 0a66 726f 6d20 6120 ut ......from a
00000030: 6269 0a6e 6172 7920 6669 6c65 2e0a 3131 bi.nary file..11
00000040: 3232 3131 3232 3131 3232 3131 3232 3131 2211221122112211
00000050: 3232 3131 3232 3131 3232 3131 3232 3131 2211221122112211
00000060: 3232 0a
次に、単純なgrep検索により、一致したシーケンスのリストが表示されます。
$ grep -o "$a" infile.hex | wc -l
2
ワンライン?
すべて1行で実行できます。
$ grep -o "ef be ad de" <(xxd -c 1 -p infile.bin | tr '\n' ' ') | wc -l
たとえば11221122
、同じファイルを検索するには、次の2つの手順が必要です。
$ a="$(printf '11221122' | hexdump -v -e '/1 "%02x "')"
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ') | wc -l
4
一致を「見る」には:
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')
3131323231313232
3131323231313232
3131323231313232
3131323231313232
$ grep "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')
…0a 3131323231313232313132323131323231313232313132323131323231313232 313132320a
バッファリング
grepがファイル全体をバッファリングし、ファイルが大きい場合、コンピューターに大きな負荷をかける懸念があります。そのために、バッファーなしのsedソリューションを使用できます。
a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin |
sed -ue 's/\('"$a"'\)/\n\1\n/g' |
sed -n '/^'"$a"'$/p' |
wc -l
最初のsedはバッファーなし(-u
)で、一致する文字列ごとにストリームに2つの改行を挿入するためにのみ使用されます。2番目sed
は、(短い)一致する行のみを印刷します。wc -lは、一致する行をカウントします。
これは、いくつかの短い行のみをバッファします。2番目のsedの一致する文字列。これは、使用されるリソースが非常に少ないはずです。
または、理解するのがやや複雑ですが、1つのsedで同じ考えです:
a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin |
sed -u '/\n/P;//!s/'"$a"'/\n&\n/;D' |
wc -l
grep -o