SC1143建议将包装的 shell 命令的注释部分包装在子 shell 中。
Posix shell 是否“足够智能”,在发现子 shell 不执行任何操作时不会启动子 shell? Bash 和 Zsh 怎么样?
答案1
作为伊尔卡丘 写,POSIX 本身指定
shell 应通过在子 shell 环境中执行命令来扩展命令替换(请参阅Shell执行环境)并替换命令替换(文本命令加上括起来的
"$()"
或反引号)与命令的标准输出,删除替换末尾的一个或多个 <newline> 字符序列。
然而,观察 shell 的实际行为会发现一些令人惊讶的事情(好吧,一个)。我使用的脚本仅包含
echo \
Before \
`# commented` \
After
Bash 和 Zsh 分叉一个子 shell 来运行仅注释命令替换:
$ strace -f -e process bash bttest
execve("/bin/bash", ["bash", "bttest"], 0x7ffe31522a10 /* 67 vars */) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDstrace: Process 3134851 attached
, child_tidptr=0x7fe3ef0c6a10) = 3134851
[pid 3134851] exit_group(0) = ?
[pid 3134851] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3134851, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG, NULL) = 3134851
wait4(-1, 0x7ffecdd13810, WNOHANG, NULL) = -1 ECHILD (No child processes)
Before After
exit_group(0) = ?
+++ exited with 0 +++
$ strace -f -e process zsh bttest
execve("/usr/bin/zsh", ["zsh", "bttest"], 0x7fffa2f78140 /* 67 vars */) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDstrace: Process 3134903 attached
, child_tidptr=0x7f236ce63750) = 3134903
[pid 3134903] exit_group(0) = ?
[pid 3134902] kill(3134903, 0) = 0
[pid 3134903] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3134903, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED|WCONTINUED, {ru_utime={tv_sec=0, tv_usec=593}, ru_stime={tv_sec=0, tv_usec=0}, ...}) = 3134903
wait4(-1, 0x7ffcabc4a7d4, WNOHANG|WSTOPPED|WCONTINUED, 0x7ffcabc4a7f0) = -1 ECHILD (No child processes)
kill(3134903, 0) = -1 ESRCH (No such process)
Before After
exit_group(0) = ?
+++ exited with 0 +++
短跑另一方面,解析反引号内的内容以确定它们是否表示内置命令或外部命令,如果解析结果为空“节点”(空命令),完全跳过它:
$ strace -f -e process dash bttest
execve("/bin/dash", ["dash", "bttest"], 0x7ffe61eee5c0 /* 67 vars */) = 0
Before After
exit_group(0) = ?
+++ exited with 0 +++
如果反引号包含命令,即使是内置命令,DASH 也会分叉,与 Bash 和 Zsh 相同;和
echo \
Before \
`: # commented` \
After
我们得到
$ strace -f -e process dash bttest
execve("/bin/dash", ["dash", "bttest"], 0x7fff762e6260 /* 41 vars */) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7effab920850) = 3905359
strace: Process 3905359 attached
[pid 3905359] exit_group(0) = ?
[pid 3905359] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3905359, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 3905359
wait4(-1, 0x7ffdb1e8683c, WNOHANG, NULL) = -1 ECHILD (No child processes)
Before After
exit_group(0) = ?
+++ exited with 0 +++
因此,至少有一个 shell 足够“智能”,在这种情况下不会启动子 shell。
有些 shell,例如 ksh93,根本不会 fork 不涉及外部命令的子 shell;即使有:
测试的变体,也strace
表明
$ strace -f -e process ksh93 bttest
execve("/bin/ksh93", ["ksh93", "bttest"], 0x7ffce3c73510 /* 41 vars */) = 0
Before After
exit_group(0) = ?
+++ exited with 0 +++
(不存在“POSIX shell”——POSIX 是一种规范,没有参考实现。据我所知,没有任何 shell 严格实现 POSIX,并且仅实现 POSIX。)