ImH*_*ere 20 shell bash subshell
根据this和this,子shell是通过使用括号启动的(…)。
( echo "Hello" )
Run Code Online (Sandbox Code Playgroud)
echo "Hello" &
Run Code Online (Sandbox Code Playgroud)
Posix 规范在本页中使用了这个词subshell,但没有定义它,而且,在同一页上,也没有定义“子进程”。
两者都使用核fork()函数,对吗?
那么将一些叉称为“子外壳”而将其他一些叉称为“子进程”的确切区别是什么。
Sté*_*las 19
在 POSIX 术语中,子外壳环境与外壳执行环境的概念相关联。
子shell 环境是作为父环境的副本创建的单独的shell 执行环境。该执行环境包括打开的文件、umask、工作目录、shell 变量/函数/别名等内容...
对该子shell 环境的更改不会影响父环境。
传统上,在 POSIX 规范所基于的 Bourne shell 或 ksh88 中,这是通过分叉子进程来完成的。
POSIX 需要或允许命令在子 shell 环境中运行的区域是传统上 ksh88 分叉子 shell 进程的区域。
然而,它并不强制实现为此使用子进程。
Shell 可以选择以他们喜欢的任何方式实现该单独的执行环境。
例如,ksh93 通过保存父执行环境的属性并在可以避免分叉的上下文中在子 shell 环境终止时恢复它们来做到这一点(作为一种优化,因为分叉在大多数系统上非常昂贵)。
例如,在:
cd /foo; pwd
(cd /bar; pwd)
pwd
Run Code Online (Sandbox Code Playgroud)
POSIX 确实需要cd /foo在单独的环境中运行并输出如下内容:
/foo
/bar
/foo
Run Code Online (Sandbox Code Playgroud)
它不需要它在单独的进程中运行。例如,如果 stdout 成为一个损坏的管道,则pwd在子 shell 环境中运行很可能会将 SIGPIPE 发送到唯一的 shell 进程。
大多数 shell 包括bash将通过评估(...)子进程中的代码来实现它(而父进程等待其终止),但 ksh93 将改为在运行内部代码时(...),所有这些都在同一进程中:
cd,保存以前的工作目录(通常在用 O_CLOEXEC 打开的文件描述符上),保存 OLDPWD、PWD 变量和任何cd可能修改的值,然后执行chdir("/bar")fchdir()在保存的fd上有一个),以及子shell可能已经修改的所有其他内容。存在无法避免子进程的上下文。ksh93 不会分叉:
var=$(subshell)(subshell)但是在
{ subshell; } &{ subshell; } | other command也就是说,事物必须在单独的进程中运行才能同时运行的情况。
ksh93 优化比这更进一步。例如,在
var=$(pwd)
Run Code Online (Sandbox Code Playgroud)
大多数shell会派生一个进程,让子进程运行pwd命令并将其stdout重定向到管道,pwd将当前工作目录写入该管道,父进程读取管道另一端的结果,ksh93虚拟化所有这些需要叉子和管子。叉子和管道只用于非内置命令。
请注意,除了子外壳之外,还有其他上下文,外壳为其分叉子进程。例如,要运行存储在单独的可执行文件中的命令(并且它不是用于同一个 shell 解释器的脚本),shell 必须派生一个进程来在其中运行该命令,否则就不会能够在该命令返回后运行更多命令。
在:
/bin/echo "$((n += 1))"
Run Code Online (Sandbox Code Playgroud)
那不是子shell,命令将在当前shell执行环境中进行评估,当前shell执行环境的n变量会递增,但是shell会fork一个子进程在其中执行该/bin/echo命令,并扩展$((n += 1))为参数.
许多 shell 实现了一种优化,因为如果它是脚本或子 shell(对于那些作为子进程实现的子 shell)的最后一个命令,它们不会派生子进程来运行该外部命令。(bash但是,只有当该命令是子外壳的唯一命令时才会这样做)。
这意味着,对于这些 shell,如果 subshell 中的最后一个命令是外部命令,则 subshell 不会导致生成额外的进程。如果比较:
a=1; /bin/echo "$a"; a=2; /bin/echo "$a"
Run Code Online (Sandbox Code Playgroud)
和
a=1; /bin/echo "$a"; (a=2; /bin/echo "$a")
Run Code Online (Sandbox Code Playgroud)
将创建相同数量的进程,仅在第二种情况下,第二个 fork 更早完成,以便a=2在 subshell 环境中运行。
小智 7
两者(子 shell 和子 shell)都是与父 shell 不同的独立进程(两者都是父 shell 的子代)。也就是说,它们有不同的PID。两者都以父 shell 的分支(副本)开始。
\n\n子 shell 是父 shell 的副本,其中变量、函数、标志和所有内容都像在父 shell 中一样可用。此类值的修改不会\xef\xbf\xbb\ 影响父级。
\n\n子 shell 作为 fork 启动,但它会重置为启动配置给定的 shell 默认值。它成为用于执行某些代码(shell 或命令)的进程。
\n\n子 shell 可以访问变量值:
\n\n$ x=123; ( echo "$x")\n123\nRun Code Online (Sandbox Code Playgroud)\n\n子 shell 不能(未导出的变量):
\n\n$ x=234; sh -c \'echo "x=$x"\'\nx=\nRun Code Online (Sandbox Code Playgroud)\n