stdinに対してループを実行し、結果を変数に格納する方法
bash(および他のシェルも)では、を介して何かを別のコマンドにパイプすると、現在のセッションの子であるサブシェルであるフォーク|
が暗黙的に作成されます。サブシェルは、現在のセッションの環境に影響を与えることはできません。
したがって、この:
TOTAL=0
printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664 |
while read A B;do
((TOTAL+=A-B))
printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
done
echo final total: $TOTAL
期待した結果は得られません!:
9 - 4 = 5 -> TOTAL= 5
3 - 1 = 2 -> TOTAL= 7
77 - 2 = 75 -> TOTAL= 82
25 - 12 = 13 -> TOTAL= 95
226 - 664 = -438 -> TOTAL= -343
echo final total: $TOTAL
final total: 0
計算されたTOTALをメインスクリプトで再利用できなかった場合。
フォークを反転させる
bash プロセス置換、ヒアドキュメント、またはヒア文字列を使用すると、フォークを逆にすることができます。
ここに文字列
read A B <<<"first second"
echo $A
first
echo $B
second
ヒアドキュメント
while read A B;do
echo $A-$B
C=$A-$B
done << eodoc
first second
third fourth
eodoc
first-second
third-fourth
ループの外側:
echo : $C
: third-fourth
ここでコマンド
TOTAL=0
while read A B;do
((TOTAL+=A-B))
printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
done < <(
printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664
)
9 - 4 = 5 -> TOTAL= 5
3 - 1 = 2 -> TOTAL= 7
77 - 2 = 75 -> TOTAL= 82
25 - 12 = 13 -> TOTAL= 95
226 - 664 = -438 -> TOTAL= -343
# and finally out of loop:
echo $TOTAL
-343
$TOTAL
これで、メインスクリプトで使用できます。
コマンドリストへのパイピング
ただし、 stdinに対してのみ機能する場合は、フォークに一種のスクリプトを作成できます。
printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664 | {
TOTAL=0
while read A B;do
((TOTAL+=A-B))
printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
done
echo "Out of the loop total:" $TOTAL
}
あげる:
9 - 4 = 5 -> TOTAL= 5
3 - 1 = 2 -> TOTAL= 7
77 - 2 = 75 -> TOTAL= 82
25 - 12 = 13 -> TOTAL= 95
226 - 664 = -438 -> TOTAL= -343
Out of the loop total: -343
注:メインスクリプト$TOTAL
では使用できませんでした(最後の右中括弧の後)。}
ラストパイプbashオプションの使用
@CharlesDuffyが正しく指摘しているように、この動作を変更するために使用されるbashオプションがあります。ただし、このためには、最初にジョブ制御を無効 にする必要があります。
shopt -s lastpipe # Set *lastpipe* option
set +m # Disabling job control
TOTAL=0
printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664 |
while read A B;do
((TOTAL+=A-B))
printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
done
9 - 4 = 5 -> TOTAL= -338
3 - 1 = 2 -> TOTAL= -336
77 - 2 = 75 -> TOTAL= -261
25 - 12 = 13 -> TOTAL= -248
226 - 664 = -438 -> TOTAL= -686
echo final total: $TOTAL
-343
これは機能しますが、これは標準ではなく、スクリプトを読みやすくするのに役立たないため、私は(個人的に)これが好きではありません。また、ジョブ制御を無効にすると、この動作にアクセスするのに費用がかかるようです。
注: ジョブ制御は、対話型セッションでのみデフォルトで有効になっています。したがってset +m
、通常のスクリプトでは必要ありません。
したがってset +m
、スクリプトで忘れると、コンソールで実行した場合とスクリプトで実行した場合に異なる動作が発生します。これでは、これを理解したりデバッグしたりするのは簡単にはなりません...