bashスクリプトを使用してバイナリファイルの内容を読み取るには?

bashスクリプトを使用してバイナリファイルの内容を読み取るには?

文字を読んだ後、固定長文字列(ファイル内でnullで終わらずに前の文字で長さが指定されています)を読みたいと思います。

Bashスクリプトでこれをどのように実行できますか?後処理を実行できるように文字列変数をどのように定義しますか?

答え1

シェルユーティリティを引き続き使用するには、head複数のバイトを抽出し、odバイトを数値に変換するために使用できます。

export LC_ALL=C    # make sure we aren't in a multibyte locale
n=$(head -c 1 | od -An -t u1)
string=$(head -c $n)

しかし、動作しませんバイナリデータの場合。 2つの質問があります。

  • コマンド置換$(…)バー最後の改行文字コマンド出力から。非常に簡単な解決策があります。出力が改行以外の文字で終わっていることを確認し、その文字を削除します。

    string=$(head -c $n; echo .); string=${string%.}
    
  • ほとんどのシェルと同様に、Bashは処理にうまくいきません。ヌルバイト。 Bash 4.1以降、コマンド置換の結果からヌルバイトが単純に削除されます。 Dash 0.5.5 と pdksh 5.2 は同じ動作を持ち、ATT ksh は最初の null バイトで読み出しを停止します。通常、シェルとそのユーティリティはバイナリファイルの処理には適していません。 (ヌルバイトをサポートするように設計されたZshは例外です。)

バイナリデータがある場合は、PerlやPythonなどの言語に切り替える必要があります。

<input_file perl -e '
  read STDIN, $c, 1 or die $!;    # read length byte
  $n = read STDIN, $s, ord($c);   # read data
  die $! if !defined $n;
  die "Input file too short" if ($n != ord($c));
  # Process $s here
'
<input_file python -c '
  import sys
  n = ord(sys.stdin.read(1))      # read length byte
  s = sys.stdin.read(n)           # read data
  if len(s) < n: raise ValueError("input file too short")
  # Process s here
'

答え2

シェルでバイナリを処理したい場合は、最良のオプションは(唯一の?)以下を使用することです。16進ダンプツール。

hexdump -v -e '/1 "%u\n"' binary.file | while read c; do
  echo $c
done

Xバイトのみを読む:

head -cX binary.file | hexdump -v -e '/1 "%u\n"' | while read c; do
  echo $c
done

長さを読み取り(長さ0を使用)、バイトの10進値で「文字列」を読み込みます。

len=$(head -c1 binary.file | hexdump -v -e '/1 "%u\n"')
if [ $len -gt 0 ]; then
  tail -c+2 binary.file | head -c$len | hexdump -v -e '/1 "%u\n"' | while read c; do
    echo $c
  done
fi

答え3

exec 3<binary.file     # open the file for reading on file descriptor 3
IFS=                   #
read -N1 -u3 char      # read 1 character into variable "char"

# to obtain the ordinal value of the char "char"
num=$(printf %s "$char" | od -An -vtu1 | sed 's/^[[:space:]]*//')

read -N$num -u3 str    # read "num" chars
exec 3<&-              # close fd 3

答え4

これはバイナリファイルをコピーします。

 while read -n 1 byte ; do printf "%b" "$byte" ; done < "$input" > "$output"

関連情報