如何对标准输入执行循环并将结果存储在变量中
在bash(以及其他shell)下,当您通过 管道将某些内容传递给另一个命令时|
,您将隐式创建一个fork,这是一个子shell,它是当前会话的子shell。子shell 不能影响当前会话的环境。
所以这:
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 Process Substitution、Here Documents或Here Strings,您可以反转分叉:
这里的字符串
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的工作,您可以在fork中创建一种脚本:
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
不能在主脚本中使用(在最后一个右大括号之后}
)。
使用lastpipe 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
因此,如果在控制台中运行或在脚本中运行,那么在脚本中被遗忘会产生不同的行为。这不会使这易于理解或调试......