在另一篇文章的评论中,@JonathanLeffler提到:
{ ... } | somecommand会在子shell中运行,并且不会影响父shell。演示:
X=PQR; echo $X; { X=ABC; echo $X; } | cat; echo $X
(输出为三行,分别是PQR、ABC、PQR)
确实如此:
james@bodacious-wired:tmp$X=PQR; echo $X; { X=ABC; echo $X; } | cat; echo $X PQR ABC PQR
然而,
man bash
中指出,{ .. }
并不会在子shell中执行:
{ list; } list is simply executed in the current shell environment. list must be terminated with a newline or semicolon. This is known as a group command.
那么这里发生了什么?
man bash
是错误的吗?我知道管道中的每个部分都在子shell中执行,但我不明白这如何导致观察到的行为。例如:
james@bodacious-wired:tmp$X=PQR; echo $X | sed; X=ABC; echo $X | sed; echo $X PQR ABC ABC
编辑后附加:
有些人建议使用
echo $$
来显示事物是否是子shell的一部分。这并不实用,因为$$
在参数扩展阶段被扩展,这发生在任何命令执行之前。举个例子:
james@bodacious-wired:tmp$echo 1$$; ps; ( echo 2$$; ps ); echo 3$$; ps 11194 PID TTY TIME CMD 1194 ttys000 0:00.22 -bash 21194 PID TTY TIME CMD 1194 ttys000 0:00.22 -bash 7894 ttys000 0:00.00 -bash 31194 PID TTY TIME CMD 1194 ttys000 0:00.22 -bash james@bodacious-wired:tmp$
你可以看到第二次调用
ps
发生在一个子shell中,pid为7894
;但是echo 2$$
仍然显示了bash在变量扩展阶段替换的值,在它生成子shell之前。作为对比,并展示
{ .. }
不会生成子shell:
james@bodacious-wired:tmp$echo 1$$; ps; { echo 2$$; ps; }; echo 3$$; ps 11194 PID TTY TIME CMD 1194 ttys000 0:00.22 -bash 21194 PID TTY TIME CMD 1194 ttys000 0:00.22 -bash 31194 PID TTY TIME CMD 1194 ttys000 0:00.23 -bash
只是为了证明 @nos 是正确的,在上面加一个管道符:
james@bodacious-wired:tmp$echo 1$$; ps; { echo 2$$; ps; } | sed ; echo 3$$; ps 11194 PID TTY TIME CMD 1194 ttys000 0:00.25 -bash 21194 PID TTY TIME CMD 1194 ttys000 0:00.25 -bash 7945 ttys000 0:00.00 -bash 7946 ttys000 0:00.00 sed 31194 PID TTY TIME CMD 1194 ttys000 0:00.25 -bash
如预期所料,shell会生成两个子shell,一个用于管道的每一侧。
{...}
,而是管道。我几乎一开始就说了这个。 - James Polleybash
版本中,{
后面需要一个空格。 - krlmlrecho $(echo $BASH_SUBSHELL)
- Lucas Cimon