Whileループでのbash
の組み込みread
関数に慣れています。例:
echo "0 1
1 1
1 2
2 3" |\
while read A B; do
echo $A + $B | bc;
done
私はいくつかのmake
プロジェクトに取り組んでおり、ファイルを分割して中間結果を保存することが賢明になりました。結果として、私はしばしば単一の行を変数に細断することになります。次の例はかなりうまく機能しますが、
head -n1 somefile | while read A B C D E FOO; do [... use vars here ...]; done
whileループが複数回実行されることはないため、これは一種の愚かなことです。しかし、while
がなければ、
head -n1 somefile | read A B C D E FOO; [... use vars here ...]
読み取り変数を使用すると、常に空になります。 read
のこの動作には気づきませんでした。通常、whileループを使用して多くの同様の行を処理するためです。 whileループなしでbash
のread
ビルトインを使用するにはどうすればよいですか?または、1行を複数の(!)変数に読み込む別の(またはさらに良い)方法はありますか?
結論
答えは私たちに教えてくれます、それはスコーピングの問題です。声明
cmd0; cmd1; cmd2 | cmd3; cmd4
コマンドcmd0
、cmd1
、cmd4
は同じスコープで実行されますが、コマンドcmd2
およびcmd3
にはそれぞれ独自のサブシェルが与えられ、その結果、スコープが異なります。元のシェルは両方のサブシェルの親です。
これは、varsを使用する部分が新しいコマンドセットであるためです。代わりにこれを使用してください:
_head somefile | { read A B C D E FOO; echo $A $B $C $D $E $FOO; }
_
この構文では、_{
_の後にスペースがあり、_;
_の前に_}
_(セミコロン)が必要です。また、_-n1
_は必要ありません。 read
は最初の行のみを読み取ります。
よりよく理解するために、これはあなたを助けるかもしれません。上記と同じです:
_read A B C D E FOO < <(head somefile); echo $A $B $C $D $E $FOO
_
編集:
次の2つのステートメントは同じことをするとよく言われます。
_head somefile | read A B C D E FOO
read A B C D E FOO < <(head somefile)
_
まあ、そうではありません。 1つ目は、head
からbash
のread
ビルトインへのパイプです。あるプロセスのstdoutから別のプロセスのstdinへ。
2番目のステートメントは、リダイレクトとプロセス置換です。 bash
自体によって処理されます。 head
の出力が接続されるFIFO(名前付きパイプ、<(...)
)を作成し、(_<
_)をread
プロセスにリダイレクトします。
これまでのところ、これらは同等に見えます。しかし、変数を操作するときは問題になる可能性があります。最初のものでは、実行後に変数が設定されません。 2つ目では、現在の環境で使用できます。
この状況では、すべてのシェルに別の動作があります。それらについては that link を参照してください。 bash
では、コマンドグループ化_{}
_、プロセス置換(< <()
)、またはヒア文字列(_<<<
_)を使用して、この動作を回避できます。
非常に役立つ記事から引用するには wiki.bash-hackers.org :
これは、パイプのコマンドが親シェルを変更できないサブシェルで実行されるためです。その結果、親シェルの変数は変更されません(記事: Bashとプロセスツリー を参照)。
答えが数回提供されているので、別の方法(組み込み以外のコマンドを使用...)は次のとおりです。
$ eval `echo 0 1 | awk '{print "A="$1";B="$2}'`;echo $B $A
$ 1 0
お気づきのように、問題はread
へのパイプがサブシェルで実行されることでした。
1つの答えはヒアドキュメントを使用することです:
numbers="01 02"
read first second <<INPUT
$numbers
INPUT
echo $first
echo $second
この方法は、POSIXのようなシェルでも同じように動作するという点で優れています。