変換せずにバイトを逐語的にコピーするために、bashでバイナリを使用するにはどうすればよいですか?


14

私は、無数の理由でc ++コードをbashに変換しようと野心的に取り組んでいます。

このコードは、完全にバイナリで記述および構造化されたサブフィールドに固有のファイルタイプを読み取り、操作します。最初のバイナリ関連のタスクは、ヘッダーの最初の988バイトをそのままコピーし、残りの情報を生成しながら書き込みを続けることができる出力ファイルに入れることです。

私の現在のソリューションは機能していないと確信しており、現実的にはこれを判断するための良い方法を見つけていません。したがって、実際に正しく記述されていても、これをテストする方法を確認する必要があります!

これは私が今やっていることです:

hdr_988=`head -c 988 ${inputFile}`
echo -n "${hdr_988}" > ${output_hdr}
headInput=`head -c 988 ${inputTrack} | hexdump`
headOutput=`head -c 988 ${output_hdr} | hexdump`
if [ "${headInput}" != "${headOutput}" ]; then echo "output header was not written properly.  exiting.  please troubleshoot."; exit 1; fi

hexdump / xxdを使用してファイルのこの部分をチェックアウトすると、ほとんどの部分を正確に読み取ることはできませんが、何かがおかしいようです。また、比較のために記述したコードは、2つの文字列が同一であるかどうかだけを示しており、希望どおりにコピーされているかどうかは示していません。

これをbashで行うより良い方法はありますか?native-binaryのバイナリバイトをそのままコピー/読み取りして、そのままファイルにコピーできますか?(そして理想的には変数としても保存すること)。


あなたは使用することができdd、個々のバイト(その設定をコピーするcountには1)。ただし、それらを保存するかどうかはわかりません。
DDPWNAGE

Cの方法でbashをしないでください。多くの頭痛の種になります。代わりに、適切なbash構造を使用します
-Ferrybig

回答:


22

シェルスクリプトで低レベルでバイナリデータを処理することは、一般的に悪い考えです。

bash変数にはバイト0を含めることはできませんzsh。そのバイトを変数に格納できる唯一のシェルです。

いずれの場合も、コマンド引数と環境変数は、execveシステムコールに渡されるNUL区切り文字列であるため、これらのバイトを含めることはできません。

次の点にも注意してください。

var=`cmd`

またはその近代的な形式:

var=$(cmd)

の出力からすべての末尾の改行文字を取り除きます cmd。そのため、そのバイナリ出力が0xaバイトで終了する場合、に格納されるときにマングルされ$varます。

ここでは、エンコードされたデータを保存する必要があります。たとえば、 xxd -p

hdr_988=$(head -c 988 < "$inputFile" | xxd -p)
printf '%s\n' "$hdr_988" | xxd -p -r > "$output_hdr"

次のようなヘルパー関数を定義できます。

encode() {
  eval "$1"='$(
    shift
    "$@" | xxd -p  -c 0x7fffffff
    exit "${PIPESTATUS[0]}")'
}

decode() {
  printf %s "$1" | xxd -p -r
}

encode var cat /bin/ls &&
  decode "$var" | cmp - /bin/ls && echo OK

xxd -p出力は1バイトを2バイトにエンコードするため、スペース効率がよくありませんが、操作(パーツの連結、抽出)を簡単に行うことができます。base644で3バイトをエンコードするものですが、操作が簡単ではありません。

ksh93シェルは、フォーマット(用途をコードする組み込み持ってbase64、あなたがそので使用できる)readおよびprintf/ printユーティリティ:

typeset -b var # marked as "binary"/"base64-encoded"
IFS= read -rn 988 var < input
printf %B var > output

シェル変数や環境変数、コマンド引数を経由しない場合、使用するユーティリティが任意のバイト値を処理できる限り問題ありません。ただし、テキストユーティリティの場合、ほとんどの非GNU実装ではNULバイトを処理できないため、マルチバイト文字の問題を回避するためにロケールをCに修正する必要があります。改行文字ではない最後の文字は、非常に長い行(2つの0xaバイトの間にあるより長いバイトのシーケンス)と同様に問題を引き起こす可能性がありますLINE_MAX

head -cバイトを使用することを意図しており、データをテキストとして扱う理由がないため、利用可能な場所はここで問題ないはずです。そう

head -c 988 < input > output

大丈夫なはず。実際には、少なくともGNU、FreeBSD、およびksh93の組み込み実装は問題ありません。POSIXは-cオプションを指定しませんがhead、任意の長さの行をサポートする必要があります(に限定されませんLINE_MAX

zsh

IFS= read -rk988 -u0 var < input &&
print -rn -- $var > output

または:

var=$(head -c 988 < input && echo .) && var=${var%.}
print -rn -- $var > output

でもでzshあれば、$varNULバイトを含んでいる、あなたはに引数として渡すことができzsh、組み込み関数(のようなprint上記)または機能ではなく、実行ファイルに渡される引数がそうであるようにNULがシェルの独立したカーネルの制限、だと、文字列を区切り、実行可能ファイルへの引数として。


zshシェル変数に1つまたは複数のNULバイトを格納できる唯一のシェルではありません。 ksh93そうすることもできます。内部的には、ksh93単にバイナリ変数をbase64エンコード文字列として保存します。
fpmurphy

@ fpmurphy1、それは私がバイナリデータの処理と呼ぶものではなく、変数にはバイナリデータが含まれていないため、たとえばシェル演算子を使用することはできません。デコードされた形式...むしろbase64エンコード/デコードサポートを組み込みと呼びます
ステファンシャゼル

11

私は、無数の理由でc ++コードをbashに変換しようと野心的に取り組んでいます。

はい、そうです。しかし、多分あなたはそれをしない非常に重要な理由を考慮する必要があります。基本的に、「bash」/「sh」/「csh」/「ksh」などはバイナリデータを処理するために設計されておらず、ほとんどの標準UNIX / LINUXユーティリティでもありません。

C ++を使用するか、Python、Ruby、Perlなどのバイナリデータを処理できるスクリプト言語を使用することをお勧めします。

これをbashで行うより良い方法はありますか?

より良い方法は、bashでそれをしないことです。


4
「bashでやらないほうがいい」という+1
Guntram BlohmはMonicaをサポートします

1
このルートを使用しないもう1つの理由は、結果のアプリケーションの実行速度が大幅に低下し、システムリソースが消費されることです。
fpmurphy

Bashパイプラインは、理解しやすいように、ドメインの特定の種類の言語として機能します。バイナリでないパイプラインについては何もありませんし、コマンドラインツールとして実装され、さまざまなユーティリティがありますそのバイナリデータと対話(ffmpegimagemagickdd)。物をつなぎ合わせるのではなくプログラミングを行うのであれば、フルパワーのプログラミング言語を使用するのがよいでしょう。
Att Righ

6

あなたの質問から:

ヘッダーの最初の988行をコピーします

988行をコピーする場合、バイナリではなくテキストファイルのように見えます。ただし、コードは988行ではなく988バイトを想定しているため、バイトが正しいと想定します。

hdr_988=`head -c 988 ${inputFile}`
echo -n "${hdr_988}" > ${output_hdr}

この部分は機能しない可能性があります。一つに${hdr_988}は、コマンドライン引数として使用し、コマンドライン引数にNULを含めることができないため、ストリーム内のNULバイトはすべて削除されます。バックティックも空白の変更を行っている可能性があります(それについてはわかりません)。(実際、echoビルトインであるため、NULの制限は適用されないかもしれませんが、それでもなお不確かだと思います。)

シェル変数を介さずに、ヘッダーを入力ファイルから出力ファイルに直接書き込むだけではどうですか?

head -c 988 "${inputFile}" >"${output_hdr}"

または、より移植性の高い、

dd if="${inputFile}" of="${output_hdr}" bs=988 count=1

bashPOSIXシェルではなくを使用していることに言及しているので、プロセス置換を使用できます。テストとしてはどうでしょうか。

cmp <(head -c 988 "${inputFile}") <(head -c 988 "${output_hdr}")

最後に、バックティックの代わりに使用することを検討してください$( ... )


通常のファイルddとは必ずしも同等ではないことに注意してくださいheadheadread(2)988バイトを取得するのに必要なだけシステムコールをdd実行しますが、1つだけを実行しますread(2)。GNU ddiflag=fullblockそのブロックを完全に読み取ろうとしますが、それはの場合よりも移植性が低くなりhead -cます。
ステファンシャゼラス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.