众所周知,在运行时bash -c "COMMAND"
(至少在 Linux 的常见版本中),当有一个命令不带任何元字符(空格、制表符或换行符除外)时,bash -c
进程不会分叉,而是通过COMMAND
直接使用execve
系统调用执行来替换自身为了优化,所以结果只会是一个过程。
$ pid=$$; bash -c "pstree -p $pid"
bash(5285)───pstree(14314)
如果存在任何元字符(例如重定向)或多个命令(无论如何都需要一个元字符),bash
则会分叉它执行的每个命令。
$ pid=$$; bash -c ":; pstree -p $pid"
bash(5285)───bash(28769)───pstree(28770)
$ pid=$$; bash -c "pstree -p $pid 2>/dev/null"
bash(5285)───bash(14403)───pstree(14404)
这是一个未记录的优化功能(这意味着它不受保证),还是在某处记录并有保证?
注意:我认为并非所有版本的bash
行为都是如此,并且在某些版本上,它只是被视为实现细节而不是保证,但我想知道是否至少有一些bash
版本明确支持这一点并记录条件为了这。例如,如果;
命令后面有一个字符,没有任何第二个命令,bash
仍然execve
不会分叉。
$ pid=$$; bash -c "pstree -p $pid ; "
bash(17516)───pstree(17658)
我的问题的背景
正如我所提到的,这种行为对于有经验的用户来说是众所周知的1 2bash
,而且我已经熟悉它很长时间了。
几天前,我在Interactive bash shell 中遇到了以下评论:通过命令行选项设置工作目录,其中 @dave_thompson_085 写道:
bash
自动执行(即用其替换自身)最后一个(或唯一的)命令-c
。
我回答说,只有当只有一个命令时,这才是正确的。但后来我想知道:是否有某些版本的bash
最后一个命令可能是 exec
ed 而不是 fork,即使它之前有另一个命令?一般来说,这种行为是否有保证?某些版本是否bash
在源代码之外公开(并详细说明)此功能?
使用
exec
是一个实现细节——如果没有记录,那么在任何版本中都不能保证该行为。根据我开发其他广泛使用的软件(Linux 内核)的经验,稍微回顾一下为什么我们倾向于不记录这些事情也许是有用的。一般来说,对于任何广泛使用的软件,人们通常会尝试仅描述其功能、操作和标准行为。人们通常会避免深入研究内部优化的细节,因为担心会产生对该行为的下游依赖。
作为我自己工作领域的一个例子,我从事内核内存管理工作,我们并不寻求详细记录(例如)回收优先级内部机制如何工作,或者何时执行 LRU 扫描等操作。记录下来将使未来关于优化和其他更改的决策变得更加复杂,因为现在我们必须考虑是否打破了一些无形的、未知的下游依赖关系。
类似地,对于 bash 或任何其他复杂的软件系统,内部机制(例如使用
exec
而不是派生新进程的决定)可能会发生变化,即使是在很短的时间内也是如此。想象一下,如果有新的性能优化、安全考虑或兼容性问题需要突然不使用 exec。我们真的想受合同约束继续以这种方式运营吗?当然不是。在 bash 和大多数其他广泛使用的项目中,非常强调维护稳定且定义良好的外部接口,该接口仅包含认为有必要公开的内容,从而允许内部实现根据需要进行发展。这种方法可确保软件保持稳健、安全和高效,而不牺牲创新和改进的能力。
所以回答你的问题,不,这没有记录,你不应该依赖它。这种情况或其他类似情况也不太可能被记录下来,因为增加内部约束会使开发变得更加严格。