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

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が、ファイルを分割して中間結果を保存するのが賢明だと思いました。その結果、1行ずつ変数に分割することが多くなりました。次の例はかなりうまくいきますが、

head -n1 somefile | while read A B C D E FOO; do [... use vars here ...]; done

whileループは1回以上実行されることはないので、これはちょっと馬鹿げています。しかしwhile

head -n1 somefile | read A B C D E FOO; [... use vars here ...]

読み取った変数は、使用時には常に空です。 のこの動作に気付いたことはありませんでした。通常、多くの類似した行を処理するには while ループを使用するからです。の組み込み関数を while ループなしでread使用するにはどうすればよいですか? または、1 行を複数の (!) 変数に読み込む別の (またはさらに良い) 方法はありますか?bashread

結論

答えは、それがスコープの問題であることを教えてくれます。

 cmd0; cmd1; cmd2 | cmd3; cmd4

は、コマンド 、 、 が同じスコープ内で実行されるように解釈されますがcmd0cmd1コマンドcmd4cmd2にはcmd3それぞれ独自のサブシェルが与えられ、結果として異なるスコープが与えられます。元のシェルは両方のサブシェルの親です。

答え1

これは、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)

headまあ、正確にはそうではありません。最初のものは、のbash組み込みへのパイプですread。あるプロセスの stdout から別のプロセスの stdin へ。

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

ここまでは、これらは同等のように見えます。しかし、変数を扱う場合には問題になることがあります。最初の例では、変数は実行後に設定されません。2 番目の例では、変数は現在の環境で使用できます。

この状況では、シェルごとに異なる動作をします。そのリンクでは、コマンドのグループ化、プロセス置換 ( )、または Here 文字列 ( )bashを使用して、この動作を回避できます。{}< <()<<<

答え2

非常に役に立つ記事から引用しますwiki.bash-hackers.org:

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

すでに何度か回答が出ていますが、別の方法(組み込みコマンド以外のコマンドを使用する方法)は次のとおりです。

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

答え3

ご指摘のとおり、問題はパイプがreadサブシェルで実行されることです。

一つの答えは、ヒアドキュメント:

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

この方法は、POSIX のようなシェルであればどこでも同じように動作するという点で便利です。

関連情報