LL3*_*LL3 5 job-control shell-script subshell
场地小
\n\n我经常使用子 shell 来执行涉及更改Shell 执行环境的操作,以免影响主 shell。我经常从交互式 shell 中执行此操作,有时也从脚本中执行此操作。
\n\n启用或禁用作业控制当然是这些操作之一,无论出于何种原因,当需要精细控制进程分组时,我一直在随意使用此功能。
\n\n然而,作为 Bash 用户,我注意到这种自由在最新版本中得到了加强:直到 v4.3,作业控制才被允许并在交互式子 shell 中完全工作,但自 v4.4 以来就不再这样了。在那里,它仍然可以在交互式子 shell 中使用,但不能完全工作(见下文)。它在脚本内仍然可以很好地工作,但是,自 v5 以来,至少一个作业控制的特定用例(即 的细粒度处理Ctrl+C)已经更加严格,使其只能从脚本内的子 shell 内进行管理..!
\n\n因此,我开始产生怀疑,因此花了一些时间对一些常见的 shell 进行了示例综合测试,所有这些测试都是在 Ubuntu 19.04 上使用其测试 shell 的库存(和发行版更新)版本执行的。
\n\n一些背景
\n\n我注意到bash, yash, mksh, 和zshdo Honorset -m在子 shell 中,而dash和kshdo 则没有。ksh即使在测试结束时被拦住也很奇怪。
TL; DR:接下来是我刚才所说的漫长的演示会议:
\n\n$ bash -c \'echo start; (set -bm; echo $-; sleep 3 & ps -s \'$$\' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end\'\nstart\nbhmBc\n PID PPID PGID TPGID SID S CMD\n 30147 30146 30147 31244 30147 S -bash\n 31241 30147 31241 31244 30147 S bash -c echo start; (set -bm; echo $-; sleep 3 & ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s\n 31242 31241 31241 31244 30147 S bash -c echo start; (set -bm; echo $-; sleep 3 & ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s\n 31243 31242 31243 31244 30147 S sleep 3\n 31244 31242 31244 31244 30147 R ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,cmd\n[1]+ Done sleep 3\nend\n$\n$\n$ dash -c \'echo start; (set -bm; echo $-; sleep 3 & ps -s \'$$\' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end\'\nstart\nbm\n PID PPID PGID TPGID SID S CMD\n 30147 30146 30147 31245 30147 S -bash\n 31245 30147 31245 31245 30147 S dash -c echo start; (set -bm; echo $-; sleep 3 & ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s\n 31246 31245 31245 31245 30147 S dash -c echo start; (set -bm; echo $-; sleep 3 & ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s\n 31247 31246 31245 31245 30147 S sleep 3\n 31248 31246 31245 31245 30147 R ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,cmd\nend # \xc2\xab\xc2\xab 3 seconds correctly elapsed before getting here\n$\n$\n$ yash -c \'echo start; (set -bm; echo $-; sleep 3 & ps -s \'$$\' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end\'\nstart\ncmb\n[1] + Running sleep 3\n PID PPID PGID TPGID SID S CMD\n 30147 30146 30147 31252 30147 S -bash\n 31249 30147 31249 31252 30147 S yash -c echo start; (set -bm; echo $-; sleep 3 & ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s\n 31250 31249 31249 31252 30147 S yash -c echo start; (set -bm; echo $-; sleep 3 & ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s\n 31251 31250 31251 31252 30147 S sleep 3\n 31252 31250 31252 31252 30147 R ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,cmd\n[1] + Done sleep 3\nend\n$\n$\n$ mksh -c \'echo start; (set -bm; echo $-; sleep 3 & ps -s \'$$\' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end\'\nstart\nmbhc\n PID PPID PGID TPGID SID S CMD\n 30147 30146 30147 31253 30147 S -bash\n 31253 30147 31253 31253 30147 S mksh -c echo start; (set -bm; echo $-; sleep 3 & ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s\n 31254 31253 31253 31253 30147 S mksh -c echo start; (set -bm; echo $-; sleep 3 & ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s\n 31255 31254 31255 31253 30147 S mksh -c echo start; (set -bm; echo $-; sleep 3 & ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s\n 31256 31254 31256 31253 30147 R ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,cmd\n[1] + Done \\sleep 3\nend\n$\n$\n$ ksh -c \'echo start; (set -bm; echo $-; sleep 3 & ps -s \'$$\' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end\'\nstart\ncbhmsB\n PID PPID PGID TPGID SID S CMD\n 30147 30146 30147 31258 30147 S -bash\n 31257 30147 31257 31258 30147 S ksh -c echo start; (set -bm; echo $-; sleep 3 & ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,\n 31258 31257 31257 31258 30147 S ksh -c echo start; (set -bm; echo $-; sleep 3 & ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,\n 31259 31258 31257 31258 30147 S ksh -c echo start; (set -bm; echo $-; sleep 3 & ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,\n 31260 31258 31257 31258 30147 R ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,cmd\n # \xe2\x80\x94\xe2\x80\x94 3 seconds correctly elapsed on this empty line (which is *not* by me) \xe2\x80\x94\xe2\x80\x94\n[1]+ Stopped ksh -c \'echo start; (set -bm; echo $-; sleep 3 & ps -s \'$$\' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end\'\n$\n$\n$ fg # \xc2\xab\xc2\xab had to get rid of Stopped `ksh` from my login shell\nksh -c \'echo start; (set -bm; echo $-; sleep 3 & ps -s \'$$\' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end\'\nend\n$\n$\n$ zsh -c \'echo start; (set -5m; echo $-; sleep 3 & ps -s \'$$\' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end\'\nstart\n569Xm\n PID PPID PGID TPGID SID S CMD\n 30147 30146 30147 31261 30147 S -bash\n 31261 30147 31261 31261 30147 S zsh -c echo start; (set -5m; echo $-; sleep 3 & ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,\n 31262 31261 31261 31261 30147 S zsh -c echo start; (set -5m; echo $-; sleep 3 & ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,\n 31263 31262 31263 31261 30147 S sleep 3\n 31264 31262 31264 31261 30147 R ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,cmd\nend # \xc2\xab\xc2\xab 3 seconds correctly elapsed before getting here\n$\nRun Code Online (Sandbox Code Playgroud)\n\n根据上述各种方法的 TPGID 值ps,在 shell 中,set -m仅尊重bash并yash在子 shell 内提供完全工作的作业控制环境,而mksh和zsh则不这样做。
事实上,在额外的测试中,mksh:
$ mksh -c \'echo start; (set -bm; echo $-; vim); echo end\'\nstart\nmbhc\nVim: Caught deadly signal HUP\nVim: Finished.\n \xc2\xab\xc2\xab \xe2\x80\x94\xe2\x80\x94 cursor stopped here. Then I hit Return \xe2\x80\x94\xe2\x80\x94\n2R1: command not found\n95: command not found\n0c: command not found\n$\nRun Code Online (Sandbox Code Playgroud)\n\n尽管zsh:
$ zsh -c \'echo start; (set -5m; echo $-; vim); echo end\'\nstart\n569Xm \xc2\xab\xc2\xab \xe2\x80\x94\xe2\x80\x94 cursor stopped here. Then I `killall zsh` from another terminal \xe2\x80\x94\xe2\x80\x94\nTerminated\n$ Vim: Caught deadly signal HUP\nVim: Finished.\n \xc2\xab\xc2\xab \xe2\x80\x94\xe2\x80\x94 cursor stopped here. Then I hit Return \xe2\x80\x94\xe2\x80\x94\n2R1: command not found\n95: command not found\n0c: command not found\n$\nRun Code Online (Sandbox Code Playgroud)\n\n当然在我杀死它的父母vim之前处于停止状态:zsh
$ mksh -c \'echo start; (set -bm; echo $-; vim); echo end\'\nstart\nmbhc\nVim: Caught deadly signal HUP\nVim: Finished.\n \xc2\xab\xc2\xab \xe2\x80\x94\xe2\x80\x94 cursor stopped here. Then I hit Return \xe2\x80\x94\xe2\x80\x94\n2R1: command not found\n95: command not found\n0c: command not found\n$\nRun Code Online (Sandbox Code Playgroud)\n\n相反,bash和yash都正确地完成了所有操作:echo start和$-,启动完全可用的vim(包括光标键和Ctrl+C),并echo end在 Vim 退出后。
然而,正如我在前提中所说的那样,当我直接从交互式 shell 中向bash它提供相同的示例子 shell 时,它崩溃了,显示了错误的 TPGID,就像和一样。在最后的测试中,只有\xc2\xa0 完全符合我的预期。mkshzshyash
最重要的是,即使我将子外壳放在外部,仅保留内部,zsh -c并且mksh -c(即非交互式)效果也不佳。只有当我把它拿走时,它们才在子壳上工作得很好。这意味着即使在脚本中也不能与这些 shell 一起工作(我实际上测试了这些脚本,但它们失败了)。set -m vimvimset -mset -m
我必须承认,这些测试显示得如此混乱,以至于我也不确定上面使用的示例测试是否有一些错误的假设。因此我还尝试了一种更(据说)无害的:
\n\n$ zsh -c \'echo start; (set -5m; echo $-; vim); echo end\'\nstart\n569Xm \xc2\xab\xc2\xab \xe2\x80\x94\xe2\x80\x94 cursor stopped here. Then I `killall zsh` from another terminal \xe2\x80\x94\xe2\x80\x94\nTerminated\n$ Vim: Caught deadly signal HUP\nVim: Finished.\n \xc2\xab\xc2\xab \xe2\x80\x94\xe2\x80\x94 cursor stopped here. Then I hit Return \xe2\x80\x94\xe2\x80\x94\n2R1: command not found\n95: command not found\n0c: command not found\n$\nRun Code Online (Sandbox Code Playgroud)\n\n它不起作用: \ trxc2\xa0got 立即停止,TPGID 坚持zsh进程。
yash它与和 一起工作bash(当然,dash它根本不尊重set -m),而不是在mksh和 上工作ksh,尽管每个结果都与 不同zsh。
最后回到我的问题
\n\n除了固有的编程复杂性之外,作业控制真的意味着在子 shell 1中得到支持吗?也可能在脚本2内?(或者:我在这里没有看到什么?)
\n\n1 POSIX 的Shell 执行环境似乎并没有禁止也没有强制执行它
\n\n2摘自POSIX 描述set:“set -m选项 [...] 主要适用于交互式使用,而不是 shell 脚本应用程序。”\n