在comment on another post中,@ JonathanLeffler表示:
{...} | somecommand在子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产生两个子壳,一个用于管道的每一侧。
答案 0 :(得分:9)
管道的每一侧至少成为一个子壳。
X=PQR; echo $X; { X=ABC; echo $X; } | cat; echo $X
将创建至少{ X=ABC; echo $X; }
和cat
的子shell /进程。
“管道中的每个命令都作为一个单独的进程执行(即在子shell中)。” ,来自man bash
如果你这样做
X=PQR; echo $X; { X=ABC; echo $X; } ; echo | cat; echo $X
之后您会看到echo $X
显示ABC。
还有其他方法可以在子壳中执行命令,例如:如果您使用子命令{ X=SUB ; sleep 1; } &
作为后台,则该组将在子shell中运行,而{ X=SUB ; sleep 1; }
则不会。
如果要对始终在子shell中执行的命令进行分组,请使用括号(X=ABC ; echo $X)
而不是大括号。
答案 1 :(得分:1)
实际上,大括号是在一个新的子shell中执行的,但仅限于管道。第一个命令是cat
,第二个命令是:
xxx@yyy:~$ ps; X=PQR; echo $X; { ps; X=ABC; echo $X; } | cat; ps; echo $X
PID TTY TIME CMD
6768 pts/7 00:00:00 bash
13158 pts/7 00:00:00 ps
PQR
PID TTY TIME CMD
6768 pts/7 00:00:00 bash
13159 pts/7 00:00:00 bash
13160 pts/7 00:00:00 cat
13161 pts/7 00:00:00 ps
ABC
PID TTY TIME CMD
6768 pts/7 00:00:00 bash
13162 pts/7 00:00:00 ps
PQR
xxx@yyy:~$ ps; X=PQR; echo $X; { ps; X=ABC; echo $X; }; ps; echo $X
PID TTY TIME CMD
6768 pts/7 00:00:00 bash
13239 pts/7 00:00:00 ps
PQR
PID TTY TIME CMD
6768 pts/7 00:00:00 bash
13240 pts/7 00:00:00 ps
ABC
PID TTY TIME CMD
6768 pts/7 00:00:00 bash
13245 pts/7 00:00:00 ps
ABC
在这种情况下,6768
是终端shell的PID,13159
是为大括号启动的子shell的PID。
如果(并且仅当)管道传输,{}
似乎在子shell中执行。