答案1
在 POSIX 术语中,子 shell 环境与以下概念相关联:Shell执行环境。
子 shell 环境是作为父环境的副本创建的单独 shell 执行环境。该执行环境包括打开的文件、umask、工作目录、shell 变量/函数/别名等内容...
对该子 shell 环境的更改不会影响父环境。
传统上,在 POSIX 规范所基于的 Bourne shell 或 ksh88 中,这是通过派生子进程来完成的。
POSIX 要求或允许命令在子 shell 环境中运行的区域是传统 ksh88 派生子 shell 进程的区域。
然而,它并不强制实现为此使用子进程。
shell 可以选择以任何他们喜欢的方式实现该单独的执行环境。
例如,ksh93 通过保存父执行环境的属性并在可以避免分叉的上下文中的子 shell 环境终止时恢复它们来实现这一点(作为一种优化,因为分叉在大多数系统上相当昂贵)。
例如,在:
cd /foo; pwd
(cd /bar; pwd)
pwd
POSIX 确实要求cd /bar
在单独的环境中运行并输出如下内容:
/foo
/bar
/foo
它不需要它在单独的进程中运行。例如,如果 stdout 成为损坏的管道,则pwd
在子 shell 环境中运行很可能会将 SIGPIPE 发送到唯一的 shell 进程。
大多数 shell 都bash
将通过评估子进程中的代码(...)
(而父进程等待其终止)来实现它,但 ksh93 会在运行内部代码时执行(...)
,所有这些都在同一个进程中:
- 请记住它是在子 shell 环境中。
- 之后
cd
,保存先前的工作目录(通常在使用 O_CLOEXEC 打开的文件描述符上),保存 OLDPWD、PWD 变量的值以及任何cd
可能修改的内容,然后执行chdir("/bar")
- 从子 shell 返回后,当前工作目录将被恢复(带有
fchdir()
保存的 fd),以及子 shell 可能已修改的其他所有内容。
在某些情况下,子进程是无法避免的。 ksh93 不分叉:
var=$(subshell)
(subshell)
但确实在
{ subshell; } &
{ subshell; } | other command
也就是说,事物必须在单独的进程中运行才能同时运行。
ksh93 的优化远不止于此。例如,当在
var=$(pwd)
大多数 shell 会分叉一个进程,让子进程运行命令pwd
,并将其 stdout 重定向到管道,pwd
将当前工作目录写入该管道,然后父进程读取管道另一端的结果,ksh93
虚拟化所有这些需要叉子或管子。叉子和管道仅用于非内置命令。
请注意,除了子 shell 之外,还有其他上下文可供 shell 派生子进程。例如,要运行存储在单独的可执行文件中的命令(并且该命令不是用于同一 shell 解释器的脚本),shell 必须分叉一个进程才能在其中运行该命令,否则就不会该命令返回后能够运行更多命令。
在:
/bin/echo "$((n += 1))"
这不是一个子shell,该命令将在当前shell执行环境中计算,n
当前shell执行环境的变量将增加,但shell将分叉一个子进程来/bin/echo
在其中执行该命令,并扩展为$((n += 1))
参数。
shell -c 'inline-script'
许多 shell 实现了一种优化,如果它是内联脚本 ( ) 或子 shell(对于那些作为子进程实现的子 shell)的最后一个命令,它们不会派生子进程来运行该外部命令。 (bash
但是,仅当该命令是子 shell 的唯一命令时才会执行此操作)。
这意味着,对于这些 shell,如果子 shell 中的最后一个命令是外部命令,则子 shell 不会导致生成额外的进程。如果比较的话:
a=1; /bin/echo "$a"; a=2; /bin/echo "$a"
和
a=1; /bin/echo "$a"; (a=2; /bin/echo "$a")
将创建相同数量的进程,仅在第二种情况下,第二个 fork 提前完成,以便a=2
在子 shell 环境中运行。
答案2
两者(子 shell 和子 shell)都是与父 shell 不同的独立进程(两者都是父 shell 的子代)。也就是说,它们有不同的PID。两者都以父 shell 的分支(副本)开始。
子 shell 是父 shell 的副本,其中变量、函数、标志和所有内容都像在父 shell 中一样可用。此类值的修改不会影响父级。
子 shell 作为 fork 启动,但它会重置为启动配置给定的 shell 默认值。它成为用于执行某些代码(shell 或命令)的进程。
子 shell 可以访问变量值:
$ x=123; ( echo "$x")
123
子 shell 不能(未导出的变量):
$ x=234; sh -c 'echo "x=$x"'
x=
答案3
子壳
子shell也称为子shell。子 shell 可以从父 shell 和另一个 shell 创建。可以使用以下命令创建子shell:
1. 进程列表
进程列表是用括号括起来的命令分组。例子:
( pwd ; (echo $BASH_SUBSHELL))
这将打印当前工作目录和生成的 shell 的数量。笔记调用 subshell 的成本很高。
2. 协同处理
它在后台模式下生成一个子 shell,并在该子 shell 中执行命令。
coproc sleep 10
如果你输入jobs
命令
[1]+ Running coproc COPROC sleep 10 &
您将看到睡眠作为后台进程在后台运行。
分叉子进程
计算中的子进程是由另一个进程创建的进程。每当执行外部命令时,就会创建一个子进程。此操作称为分叉。
$ps -f
UID PID PPID C STIME TTY TIME CMD
umcr7 3647 3638 0 13:54 pts/0 00:00:00 bash
umcr7 3749 3647 0 13:59 pts/0 00:00:00 ps -f
与ps -f
外部命令一样(即外部命令,有时称为文件系统命令,是存在于 bash shell 之外的程序。)这将创建具有执行该命令的 bash shell 的父 id 的子进程。