Bash子shell/管道-哪些部分在子shell中执行?

Bash子shell/管道-哪些部分在子shell中执行?,bash,Bash,在一份声明中,@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 但是,manbash表示,

在一份声明中,@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
但是,
manbash
表示,
{..}
不会在子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. 
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
这是怎么回事?
manbash
错了吗?我知道管道的每个部分都在子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
;但是
echo2$$
仍然显示bash在生成子shell之前在变量扩展阶段替换的值

作为对比,并演示
{..}
不会生成子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. 
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,管道的每侧各有一个子shell。

实际上,大括号是在一个新的子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
是终端外壳的PID,
13159
是为支架启动的子外壳的PID


似乎当(且仅当)管道化时,
{}
在子shell中执行。

管道的每一侧至少成为子shell

X=PQR; echo $X; { X=ABC; echo $X; } | cat; echo $X
将生成至少
{X=ABC;echo$X;}
cat
的子shell/进程

“管道中的每个命令都作为单独的进程执行(即,在子shell中)。”,来自ManBash

如果你改为这样做

X=PQR; echo $X; { X=ABC; echo $X; } ; echo | cat; echo $X
之后您将看到,
echo$X
显示ABC

在子shell中执行命令还有其他方式,例如,如果您将子命令设置为背景:
{X=SUB;sleep 1;}&
,则该组将在子shell中运行,而仅
{X=SUB;sleep 1;}
不会


如果您想对总是在子shell中执行的命令进行分组,请使用括号,而不是大括号。

我想我们赢了。创建子shell的不是
{..}
,而是管道。实际上,我一开始就直截了当地说了。在
{
之后需要一个空格,至少在我的
bash
$(cmd args)版本中,构造还需要创建一个子shell:
echo$(echo$bash\u subshell)
管道创建子shell,而不是大括号。不,不是管道,而是与管道连接的大括号。检查
ps
vs.
ps | cat
vs.
{ps;}cat
的输出。大括号从不调用子shell!只有管道才会这样做: