私は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 行を複数の (!) 変数に読み込む別の (またはさらに良い) 方法はありますか?bash
read
結論
答えは、それがスコープの問題であることを教えてくれます。
cmd0; cmd1; cmd2 | cmd3; cmd4
は、コマンド 、 、 が同じスコープ内で実行されるように解釈されますがcmd0
、cmd1
コマンドcmd4
とcmd2
には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 のようなシェルであればどこでも同じように動作するという点で便利です。