创建子 shell 的 shell 是否需要 () Groups 命令?

创建子 shell 的 shell 是否需要 () Groups 命令?

在我的书中(索贝尔的Linux 实用指南, 4e) 文中写道

您可以使用括号控制运算符对命令进行分组。当您使用此技术时,shell 会为每个组创建一个自身的副本,称为子 shell。它将每组命令视为一个列表,并创建一个新进程来执行每个命令......

我不想错误地解释这一点,所以我想我会在这里问。是否创建子shell一定需要使用这些 () 组命令,还是这只是确保某些命令在同一子销售中运行的一种方法?

也许让我举例问一下。假设我有命令( 中的可执行文件PATHab.在命令提示符下输入以下内容有什么区别吗?

  1. a ; b

  2. (a ; b)

  3. (a) ; (b)

答案1

a相等foo=xyz,并且b相等echo $foo。或者更确切地说,让我们将它们定义为函数:

a() { foo=xyz; }
b() { echo $foo; }

然后,让我们尝试您显示的每个变体,在每种情况下,初始化foo为第一个,并在最后abc打印 的值。foo右侧输出:

  1. foo=abc; a ; b; echo $foo=> xyz,xyz
  2. foo=abc; (a ; b); echo $foo=> xyz,abc
  3. foo=abc; (a) ; (b); echo $foo=> abc,abc

因此,在第一个中,分配发生在主级别,因此对脚本的其余部分可见。 (这些函数使用{ ..; }分组构造,因此它们在主 shell 中运行。)在第二个中,赋值发生在与第一个打印输出相同的子 shell 中,但不会影响脚本的其余部分。在第三个中,赋值发生在第一个子 shell 中,并且仅在该子 shell 中可见,而在脚本的后面部分不可见。

话又说回来,您询问了 中的可执行文件PATH,并且由于它们无论如何都不会影响 shell 的执行环境,因此它们是否在子 shell 中运行并不重要。即,ls与 相同(ls)。但对于 shell 内置函数来说,差异很重要。考虑例如read(设置变量)或exit(退出(子)shell)。


命令替换也在子 shell 中运行,因此例如

foo=abc
echo $(foo=xyz; echo $foo)
echo $foo

打印xyzabc.

当然,命令替换语法也使用括号,因此存在一定的对称性。 (话又说回来,(( ... ))这是完全不同的事情。)


任何与 shell 异步或并发运行命令的操作也必然会启动子 shell,因为这样做需要生成一个无法修改主 shell 进程的新进程。

一个常见的情况是管道。在 中foo | bar | doo, 和 都foobar子 shell 中运行,并且doo可能在子 shell 中运行,也可能在主 shell 环境中运行。

例如

foo=abc
{ foo=xyz; echo $foo; } | cat
echo $foo

印刷xyzabc

看:为什么我的变量在一个“while read”循环中是本地变量,但在另一个看似相似的循环中却不是?

foo &显然,使用、 或进程替换 ( <( foo )) 或其他类似方式在后台显式运行某些内容也会启动子 shell。


无论如何,它( .. )是为了启动一个子外壳而显式启动一个子外壳的。对于其他人,我们可以说这是一种副作用。

答案2

这三行做了不同的事情。

[me@here foo]$ echo A-$BASHPID ; echo B-$BASHPID
A-534171
B-534171
[me@here foo]$ (echo A-$BASHPID ; echo B-$BASHPID)
A-534798
B-534798
[me@here foo]$ (echo A-$BASHPID) ; (echo B-$BASHPID)
A-534808
B-534809

首先从当前 shell 的上下文中执行 a,然后执行 b(本例中 pid=534171)。第二个创建一个新的子 shell (pid=534798),然后在该新子 shell 中执行 a,然后执行 b。第三个创建一个新的子 shell (pid=534808) 并在其中执行 a。该子 shell 退出后,它会创建另一个子 shell (pid=534809) 并在该子 shell 中执行 b。这些子 shell 通常不会从原始 shell 继承其整个环境(例如未导出的 shell 变量、文件描述符、'ERR、DEBUG、RETURN 陷阱处理是特殊情况),并且子 shell 环境的某些部分被显式更改(进程 IDS、外壳历史...)。在子 shell 中执行的命令通常也不可能修改父 shell 环境。

[me@here foo]$ A=foo
[me@here foo]$ A=bar
[me@here foo]$ (A=baz)
[me@here foo]$ echo $A
bar

甚至二进制可执行文件也是从这些子 shell 启动的可以发现差异并采取不同的行为。

相关内容