如何对 stdin 执行循环并将结果存储在变量中
在bash(以及其他shell)下,当您通过| 将某些内容传递给另一个命令时,您将隐式创建一个fork,这是一个子shell,它是current 的子shell会议。 subshell 不能影响当前会话的环境。
所以这个:
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 不能在主脚本中重复使用。
倒叉
通过使用bashProcess Substitution、Here Documents 或 Here Strings,您可以反转 fork:
这里是字符串
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 会产生不同的行为。这不会使其易于理解或调试...