whileループなしでbashの読み取り組み込みコマンドを使用する

whileループなしでbashの読み取り組み込みコマンドを使用する

私は次のwhileループの組み込み関数に精通していますbashread

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独自のサブシェルが提供されるため、スコープは異なります。元のシェルは、2つのサブシェルの親シェルです。cmd4cmd2cmd3

答え1

変数を使う部分が新しい命令集合だからだ。代わりにこれを使用してください:

head somefile | { read A B C D E FOO; echo $A $B $C $D $E $FOO; }

この構文では、後ろにスペースを入れ、前に{ (セミコロン)を付ける必要があります。必ずしも必要ではありません。最初の行だけを読みます。;}-n1read

より良い理解のために、上記と同じことをするのに役立ちます。

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。あるプロセスの標準出力は別のプロセスの標準入力に移動されます。

2番目の説明は、リダイレクトとプロセスの交換です。bash独自に処理されます。 FIFO(パイプという名前<(...))を作成し、headその出力をFIFOに接続してからプロセスに<リダイレクトします。read

これまでは、これらが同じように見えます。ただし、変数を使用する場合はこれが重要です。まず、実行後の変数は設定されません。第二に、現在の環境で使用できます。

この状況では、各シェルは異なる動作をします。バラよりそのリンク彼らはこれのためにここにあります。コマンドのグループ化、プロセス置換()、またはここで文字列()を使用してbashこの動作を解決できます。{}< <()<<<

答え2

非常に便利な記事から引用wiki.bash-hackers.org:

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

回答が複数回提供されたので、他の方法(組み込みのコマンドを使用して...)は次のようになります。

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

答え3

指摘したように、問題はパイプがreadサブシェルで実行されることです。

1つの答えは、次を使用することです。トレドック:

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

このアプローチは、POSIX と同様のシェルで同じように動作するため、良いです。

関連情報