Bash subshel​​l / pipelines - 哪些部分在子shell中执行?

时间:2012-02-02 08:47:28

标签: bash

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产生两个子壳,一个用于管道的每一侧。

2 个答案:

答案 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中执行。