关于 BASH 和其他 UNIX shell,我注意到的一件事是,默认情况下以及以典型方式使用时,它们会为几乎所有内容生成子 shell。
例如
foo=$(grep "someword" /path/to/somefile | awk '{print $3}')
会产生两个新的 bash 会话,只是为了将一些文本加载到变量中。
a) 为什么 shell 会这样做?如果命令行程序对文件描述符进行操作,则不需要生成一个新的非交互式 shell 会话只是为了向其提供描述符,对吗?
b) 将命令输出加载到变量中时,如何最好地解决这个问题?我知道在 bash 中你可以使用:
read -r -d '' < <(...)
在不使用子shell的情况下从命令设置变量,但这相当麻烦,我正在寻找更好的(和更便携的)方法。 (同样,如果有人知道管道和命令替换的一般替代方案,那就太酷了不是涉及子shell。)
注意:我知道“用户 Perl/Python/Ruby”可能是“正确”的解决方案,但这些解决方案往往需要大量样板代码来进行文件操作、外部命令调用等。
编辑:感谢下面的答案,但这仍然不能解释为什么进程替换必须分叉新的 shell即使对于内置命令:
$ builtin echo $(builtin echo $(builtin echo $BASH_SUBSHELL))
2
答案1
grep
要执行诸如或 之类的命令awk
,shell 必须 fork,这意味着您将获得一个子 shell。唯一的例外是当该命令是内置命令或该命令是最后一个命令时。但后一种情况只是某些 shell 所做的优化,在某些可能改变行为的条件下(例如现有陷阱)无法完成;基本上这是一个隐含的exec
。因此你的两个子shell
foo=$(grep "someword" /path/to/somefile | awk '{print $3}')
为了避免子shell,有多种解决方法,您需要根据上下文找到它们......
答案2
重点关注a):
执行命令
foo=$(echo bar)
启动子 shell,因为该命令正在使用命令替换,这意味着子 shell 作为所使用的环境。
命令显式运行子shell, 就如此容易。
如果你嵌套它,这一点也不会改变。
现在,在回答了“为什么 shell 这样做”这个问题之后:
你为什么问这个?
这是一个有趣的话题,但我不完全明白你的目标是什么;
一些疯狂的猜测:
- 提出优化 bash 实现?
- 减少叉子数量?出于性能原因?出于某种审美原因?这是提供独立环境(包括地址空间 - 进程)的最快方法。
- 改变 shell 的语法?到底要达到什么目标呢?
如果您愿意,请一些人为问题添加详细信息,然后给我留言。
COMMAND EXECUTION ENVIRONMENT
[ ... ]
Command substitution, commands grouped with parentheses, and asyn‐
chronous commands are invoked in a subshell environment that is a
duplicate of the shell environment, except that traps caught by the
shell are reset to the values that the shell inherited from its par‐
ent at invocation. Builtin commands that are invoked as part of a
pipeline are also executed in a subshell environment. Changes made
to the subshell environment cannot affect the shell's execution envi‐
ronment.
[ ... ]