web-dev-qa-db-ja.com

入力をBash whileループにパイプし、ループ終了後に変数を保持する方法

Bashでは次を使用できます:cat <(echo "$FILECONTENT")

Bashは次の使用も許可します:while read i; do echo $i; done </etc/passwd

前の2つを組み合わせるために、これを使用できます:echo $FILECONTENT | while read i; do echo $i; done

最後の問題は、サブシェルを作成し、whileループの終了後に変数iにアクセスできなくなることです。

私の質問は:

このようなことを達成する方法:while read i; do echo $i; done <(echo "$FILECONTENT")または言い換えると:iがwhileループで生き残ることをどのように確認できますか?

Whileステートメントを{}で囲むことは承知していますが、これでは問題は解決しません(関数でwhileループを使用してi変数を返すと想像してください)

71
Wakan Tanka

Process Substitution の正しい表記は次のとおりです。

while read i; do echo $i; done < <(echo "$FILECONTENT")

ループで割り当てられたiの最後の値は、ループの終了時に使用可能になります。別の方法は次のとおりです。

echo $FILECONTENT | 
{
while read i; do echo $i; done
...do other things using $i here...
}

中括弧はI/Oグループ化操作であり、サブシェル自体は作成しません。このコンテキストでは、これらはパイプラインの一部であり、したがってサブシェルとして実行されますが、これは|ではなく{ ... }が原因です。あなたは質問でこれに言及します。私の知る限り、関数内でこれらの中から戻ることができます。


Bashは shopt ビルトインも提供し、その多くのオプションの1つは次のとおりです。

lastpipe

設定され、ジョブ制御がアクティブでない場合、シェルは現在のシェル環境のバックグラウンドで実行されていないパイプラインの最後のコマンドを実行します。

したがって、スクリプトでこのようなを使用すると、変更されたsumがループの後に使用可能になります。

FILECONTENT="12 Name
13 Number
14 Information"
shopt -s lastpipe   # Comment this out to see the alternative behaviour
sum=0
echo "$FILECONTENT" |
while read number name; do ((sum+=$number)); done
echo $sum

これをコマンドラインで実行すると、通常、「ジョブコントロールがアクティブではありません」というファウルが発生します(つまり、コマンドラインではジョブコントロールがアクティブになります)。スクリプトを使用せずにこれをテストすると失敗しました。

また、 answerGareth Rees で示されているように、 here string を使用することもできます。

while read i; do echo $i; done <<< "$FILECONTENT"

これにはshoptは必要ありません。それを使用してプロセスを保存できる場合があります。

92

Jonathan Lefflerが説明します 使用したいことを行う方法 process substitution ですが、別の可能性は here string を使用します。

while read i; do echo "$i"; done <<<"$FILECONTENT"

これによりプロセスが節約されます。

26
Gareth Rees