web-dev-qa-db-ja.com

whileループなしでbashのread組み込みを使用する

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ループなしでbashreadビルトインを使用するにはどうすればよいですか?または、1行を複数の(!)変数に読み込む別の(またはさらに良い)方法はありますか?

結論

答えは私たちに教えてくれます、それはスコーピングの問題です。声明

 cmd0; cmd1; cmd2 | cmd3; cmd4

コマンドcmd0cmd1cmd4は同じスコープで実行されますが、コマンドcmd2およびcmd3にはそれぞれ独自のサブシェルが与えられ、その結果、スコープが異なります。元のシェルは両方のサブシェルの親です。

8
Bananguin

これは、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からbashreadビルトインへのパイプです。あるプロセスのstdoutから別のプロセスのstdinへ。

2番目のステートメントは、リダイレクトとプロセス置換です。 bash自体によって処理されます。 headの出力が接続されるFIFO(名前付きパイプ、<(...))を作成し、(_<_)をreadプロセスにリダイレクトします。

これまでのところ、これらは同等に見えます。しかし、変数を操作するときは問題になる可能性があります。最初のものでは、実行後に変数が設定されません。 2つ目では、現在の環境で使用できます。

この状況では、すべてのシェルに別の動作があります。それらについては that link を参照してください。 bashでは、コマンドグループ化_{}_、プロセス置換(< <())、またはヒア文字列(_<<<_)を使用して、この動作を回避できます。

8
chaos

非常に役立つ記事から引用するには wiki.bash-hackers.org

これは、パイプのコマンドが親シェルを変更できないサブシェルで実行されるためです。その結果、親シェルの変数は変更されません(記事: Bashとプロセスツリー を参照)。

答えが数回提供されているので、別の方法(組み込み以外のコマンドを使用...)は次のとおりです。

$ eval `echo 0 1 | awk '{print "A="$1";B="$2}'`;echo $B $A
$ 1 0
3
geedoubleya

お気づきのように、問題はreadへのパイプがサブシェルで実行されることでした。

1つの答えはヒアドキュメントを使用することです:

numbers="01 02"
read first second <<INPUT
$numbers
INPUT
echo $first
echo $second

この方法は、POSIXのようなシェルでも同じように動作するという点で優れています。

0
eradman