奇怪的行为(可能还有其他的shell)?

当我做:

/bin/bash -c 'cat /proc/$$/cmdline' 

我得到的输出是:

 cat/proc/25050/cmdline 

而我预期的产出是:

 /bin/bash -c 'cat /proc/$$/cmdline' 

另一方面,当我这样做的时候:

 /bin/bash -c 'echo $$; cat /proc/$$/cmdline' 

我得到了预期的输出结果,即:

 28259 /bin/bash-cecho $$; cat /proc/$$/cmdline 

好像$$是猫的pid,而不是bash / sh的pid。
为什么是这样?
shell是否执行某种parsing和execve()样式replace? 如果是这样,它甚至在更换之前如何知道猫的PID?

为了理解这个行为,我们必须确定bash如何在命令行上执行传递给它的命令的。 关键是如果命令足够简单, 就没有fork (或clone或类似的东西)。

 $ strace -f -e clone,execve /bin/bash -c 'cat /proc/$$/cmdline' execve("/bin/bash", ["/bin/bash", "-c", "cat /proc/$$/cmdline"], [/* 80 vars */]) = 0 execve("/bin/cat", ["cat", "/proc/2942/cmdline"], [/* 80 vars */]) = 0 cat/proc/2942/cmdline+++ exited with 0 +++ $ 

OTOH如果命令比较复杂, bash叉子:

 $ strace -f -e clone,execve /bin/bash -c 'echo $$; cat /proc/$$/cmdline' execve("/bin/bash", ["/bin/bash", "-c", "echo $$; cat /proc/$$/cmdline"], [/* 80 vars */]) = 0 2933 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7ff64e6779d0) = 2934 Process 2934 attached [pid 2934] execve("/bin/cat", ["cat", "/proc/2933/cmdline"], [/* 80 vars */]) = 0 /bin/bash-cecho $$; cat /proc/$$/cmdline[pid 2934] +++ exited with 0 +++ --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=2934, si_uid=1000, si_status=0, si_utime=0, si_stime=0} --- +++ exited with 0 +++ $ 

好像$$是猫的pid,而不是bash / sh的pid。

它实际上是两个。 直接execvecat ,所以一个成为另一个。

为了理解无叉行为究竟需要什么,我们需要查看源代码。 有这样的评论:

  /* * IF * we were invoked as `bash -c' (startup_state == 2) AND * parse_and_execute has not been called recursively AND * we're not running a trap AND * we have parsed the full command (string == '\0') AND * we're not going to run the exit trap AND * we have a simple command without redirections AND * the command is not being timed AND * the command's return status is not being inverted * THEN * tell the execution code that we don't need to fork */ 

资源