我一直在试图读取输入这样从程序输出的环境变量:
echo first second | read A B ; echo $A-$B
其结果是:
-
A和B总是空的。 我读到的bash在子壳执行管道命令和基本上预防一种从管道输入到读出。 然而,以下内容:
echo first second | while read A B ; do echo $A-$B ; done
似乎工作,其结果是:
first-second
可有人请解释什么是这里的逻辑是什么? 难道里面的命令while
... done
结构在同一个shell作为实际执行的echo
,而不是在一个子shell?
如何做一个循环对标准输入和获得的结果存储在变量
在庆典 (和其他外壳也),当你管什么用|
另一个命令,你会隐含创建一个叉 ,一个子shell谁是本届会议的一个孩子,谁也不能影响当前会话的ENVIRON。
所以这:
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
当计算总 could'nt在主脚本被重用。
反相叉
通过使用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
在你的主脚本 。
管道连接到命令列表
但对于工作仅针对标准输入 ,您可以创建一种脚本进叉的:
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
在脚本运行,如果在一个控制台或如果在脚本中运行会产生不同的行为。 这会不会让这个容易理解和调试...
首先,该管链被执行:
echo first second | read A B
然后
echo $A-$B
因为read AB
在子Shell被执行时,A和B都将丢失。 如果你这样做:
echo first second | (read A B ; echo $A-$B)
那么这两个read AB
和echo $A-$B
在同一子shell(见的bash的手册页,搜索执行(list)
更清洁的变通......
read -r a b < <(echo "$first $second")
echo "$a $b"
通过这种方式,读取不在一个子shell(其将尽快为子shell结束清除变量)执行。 相反,你要使用的变量回荡在一个子shell自动继承父shell变量。
你看到的是进程之间的分离:在read
在子shell发生-单独的过程不能改变主处理(其中变量echo
命令后发生)。
流水线(如A | B
)隐式地在子壳的每个组件(一个独立的进程),即使是内置插件(比如read
),通常在shell的上下文中运行(在同一个进程)。
在“管道进入而”的情况下,所不同的是一种错觉。 该规则同样适用有:循环是管道下半年,所以它是在子shell,但整个循环是在同一子shell,这样的过程分离不适用。
文章来源: Why piping input to “read” only works when fed into “while read …” construct?