如果我是正确的,命令替换、进程替换、管道和后台作业将在子 shell 中而不是在原始 shell 中运行它们自身指定的命令。
但是当命令为jobs
或时dirs
,它们的输出与直接在原始 shell 中运行时的输出完全相同:
echo $( jobs )
cat <(jobs)
jobs | less
echo $( dirs )
cat <(dirs)
dirs | less
dirs &
这是为什么?是因为子 shell 继承了原始 shell 中的作业和目录堆栈,还是因为jobs
和dirs
在原始 shell 中而不是子 shell 中运行?
但
jobs &
不输出任何内容。为什么它与其他命令不同?谢谢。
答案1
这个答案的第一部分是 和jobs
是dirs
内置的,所以通常 bash 只会直接运行它们,而不是调用子 shell。
$(jobs)
对于存在管道或命令替换 ( ) 或进程替换 ( )的情况也是如此<(jobs)
。
jobs &
在后台运行命令 ( ) 或显式请求子 shell (使用( jobs )
.)的情况并非如此。
这就解释了你问题的最后一部分。当您jobs
在子 shell 中运行时,它确实会显示子 shell 本身的作业。
这是这个概念的一个很好的演示:
$ sleep 1001 &
[1] 15927
$ sleep 1002 &
[2] 15940
$ jobs
[1]- Running sleep 1001 &
[2]+ Running sleep 1002 &
$ ( sleep 1003 & jobs )
[1]+ Running sleep 1003 &
$ jobs
[1]- Running sleep 1001 &
[2]+ Running sleep 1002 &
您将看到在子 shell 中运行的情况下,只会显示jobs
该子 shell 的作业(在本例中)。sleep 1003
现在,总结一下,我们需要解决问题的中间部分,这就是为什么dirs &
(它确实在子 shell 中运行,就像那样( dirs )
)仍然会显示保存在 Pushd 堆栈中的目录。
事实证明,这是因为 shell 导出特殊数组变量 中的目录列表DIRSTACK
,然后由子 shell 继承。 (您可以阅读更多关于DIRSTACK
这里.)
演示其工作原理的一种方法是pushd
在子 shell 上使用,并查看它如何不会影响原始 shell 的目录堆栈:
$ dirs
~
$ pushd ~/tmp
~/tmp ~
$ ( dirs; pushd / >/dev/null; dirs )
~/tmp ~
/ ~/tmp ~
$ dirs
~/tmp ~
我相信这应该解决您询问的所有问题。
更新:bash 有一个特殊的规定可以让jobs | less
命令jobs
在当前 shell 中运行(其他内置命令则不然。)
该代码有一个存储在jobs_hack
变量中的检查,稍后会检查该变量以使其在不创建子 shell 的情况下运行,并允许将输出通过管道传输jobs
到另一个命令。
看这里了解实现此功能的源代码的相关部分。 (从 bash 4.4 开始)