为什么“jobs”和“dirs”在命令替换、进程替换、管道和后台作业中运行时输出与原始 shell 中的输出相同?

为什么“jobs”和“dirs”在命令替换、进程替换、管道和后台作业中运行时输出与原始 shell 中的输出相同?

如果我是正确的,命令替换、进程替换、管道和后台作业将在子 shell 中而不是在原始 shell 中运行它们自身指定的命令。

但是当命令为jobs或时dirs,它们的输出与直接在原始 shell 中运行时的输出完全相同:

echo $( jobs )

cat <(jobs)

jobs | less

echo $( dirs )

cat <(dirs)

dirs | less

dirs &

这是为什么?是因为子 shell 继承了原始 shell 中的作业和目录堆栈,还是因为jobsdirs在原始 shell 中而不是子 shell 中运行?

jobs &

不输出任何内容。为什么它与其他命令不同?谢谢。

有关的在子 shell 中获取脚本是否仍允许脚本中的命令访问原始 shell 的状态?

答案1

这个答案的第一部分是 和jobsdirs内置的,所以通常 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 开始)

相关内容