bashパイプで生のバイナリデータを処理するにはどうすればよいですか?


15

パラメータとしてファイルを受け取り、ファイルが存在することを確認してから、stdinから出てくるものをすべてファイルに書き込むbash関数があります。素朴な解決策はテキストに対してはうまく機能しますが、任意のバイナリデータに問題があります。

echo -n '' >| "$file" #Truncate the file
while read lines
do  # Is there a better way to do this? I would like one...
    echo $lines >> "$file"
done

回答:


15

あなたの方法は、区切り記号($IFS)が読み取りを分割するために使用しているスペースに書き込むすべてのものに改行を追加することです。それを改行に分割するのではなく、全部を取り出して渡すだけです。上記のコード全体をこれに減らすことができます:

 cat - > $file

切り捨てビットは必要ありません。これにより、STDINストリーム全体が切り捨てられて書き込まれます。

編集: zshを使用> $fileしている場合は、猫の代わりに使用できます。ファイルにリダイレクトして切り詰めていますが、何かがSTDINを受け入れるのを待っている場合は、その時点で読み込まれます。bashを使用してこのようなことができると思いますが、特別なモードを設定する必要があります。


stdinリダイレクトの例を機能させることはできませんでしたが、catの例を> |に変更しました (私はnoclobberセットを持っています)チャームのように動作します。私の一日を作ってくれてありがとう^。^
デビッド・サウザー

猫なしバージョンの場合は+1。常に役に立たない猫を避けてください;)
rozcietrzewiacz

@rozcietrzewiacz:確かに、それは後付けであり、間違っていた。これは猫の無駄な使用ではないかもしれません。あなたができる唯一のことは> $file。これは、親シェルスクリプトで標準入力を最初に探す場合にのみ機能します。基本的に、Davidのコードはすべて1文字に減らすことができますがcat -、一見して理解できるので、よりエレガントでトラブルが少ないと思います。
カレブ

時々私の文字列4または5 catだけ困らせるのUUOCの狂信秒一緒に、
マイケルMrozek

@MichaelMrozek:時々、データファイルに名前を付けるのは、catそれを使用することを主張する人々がコードを読むために必ず精神的な体操をしなければならないからです。名前付きパイプも良いターゲットです。
カレブ

7

テキストファイルを文字通り読み取るには、次のread2つの方法で出力を処理するplainを使用しないでください。

  • read\エスケープ文字として解釈します。read -rこれをオフにするために使用します。
  • read$IFS;の文字で単語に分割されます。IFSこれをオフにするには、空の文字列に設定します。

テキストファイルを1行ずつ処理する通常のイディオムは

while IFS= read -r line; do 

このイディオムの説明については、「ではなく、なぜwhile IFS= read頻繁に使用されるのかIFS=; while read..」を参照してください

文字列を文字通りに書くには、文字echo列を2つの方法で処理するplainを使用しないでください。

  • 一部のシェルでは、echoバックスラッシュエスケープを処理します。(bashでは、xpg_echoオプションが設定されているます。)
  • いくつかの文字列はオプションとして扱われます。たとえば、-nまたは-e(正確なセットはシェルによって異なります)。

文字列を文字通り印刷するポータブルな方法は、printfです。(bashには、入力がのオプションのように見えないことがわかっている場合を除き、より良い方法はありませんecho。)最初のフォームを使用して正確な文字列を印刷し、改行を追加する場合は2番目のフォームを使用します。

printf %s "$line"
printf '%s\n' "$line"

これは、テキストの処理にのみ適しています

  • ほとんどのシェルは、入力内のヌル文字で窒息します。
  • 最後の行を読んだとき、最後に改行があったかどうかを知る方法はありません。(入力が改行で終わらない場合、一部の古いシェルではより大きな問題が発生する可能性があります。)

シェルでバイナリデータを処理することはできませんが、ほとんどの大学の最新バージョンのユーティリティは任意のデータに対処できます。すべての入力を出力に渡すには、を使用しますcat。接線に沿って進むことecho -n ''は、何もしない複雑で移植性のない方法です。echo -n(シェルに依存しない)同じくらい良いでしょうし、:よりシンプルで完全に移植可能です。

: >| "$file"
cat >>"$file"

または、より単純な、

cat >|"$file"

スクリプトでは、デフォルトではオフになっているため>|、通常は使用する必要はありませんnoclobber


xpg_echoを指摘してくれてありがとう、それは実際に私が自分のコードのどこかに持っていて気づかなかった問題です。noclobberについて、私は私のbashrcでそれをオンにする習慣にあります。
デビッドサウザー

0

これはまさにあなたが望むことをします:

( while read -r -d '' ; do
    printf %s'\0' "${REPLY}" ;
  done ;

  # When read hits EOF, it returns non-zero which exits the while loop.
  # That data still needs to be output:
  printf %s "${REPLY}"
) >> ${file}

ただし、メモリ使用量に注意してください。これは、ヌル区切り形式で入力を読み取ります。

入力に\0 nullバイトがない場合、bashは最初に入力の内容全体をメモリに読み込んでから出力する必要があります。

切り捨て手順について:

echo -n '' >| "$file" #Truncate the file

より単純で同等なものは次のとおりです。

> ${file}   #Truncate the file
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.