场地小
我经常使用子 shell 来执行涉及更改Shell执行环境,以免影响主壳。我经常从交互式 shell 中执行此操作,有时也从脚本中执行此操作。
启用或禁用作业控制当然是这些操作之一,无论出于何种原因,当需要精细控制进程分组时,我一直在随意使用此功能。
然而,作为 Bash 用户,我注意到这种自由在最新版本中得到了加强:直到 v4.3,作业控制才被允许并在交互式子 shell 中完全工作,但自 v4.4 以来就不再这样了。在那里,它仍然可以在交互式子 shell 中使用,但不能完全工作(见下文)。它在脚本中仍然可以很好地工作,但是,自 v5 以来,至少一个作业控制的特定用例(即 的细粒度处理Ctrl+C)已经更加严格,使其易于管理仅在脚本内的子 shell 中..!
因此,我开始产生怀疑,因此花了一些时间对一些常见的 shell 进行了示例综合测试,所有这些测试都是在 Ubuntu 19.04 上使用其测试 shell 的库存(和发行版更新)版本执行的。
一些背景
我注意到bash
, yash
, mksh
, 和zsh
do Honorset -m
在子 shell 中,而dash
和ksh
do 则没有。ksh
即使在测试结束时被拦住也很奇怪。
TL;博士:接下来是我刚才所说的冗长的演示会议:
$ bash -c 'echo start; (set -bm; echo $-; sleep 3 & ps -s '$$' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end'
start
bhmBc
PID PPID PGID TPGID SID S CMD
30147 30146 30147 31244 30147 S -bash
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
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
31243 31242 31243 31244 30147 S sleep 3
31244 31242 31244 31244 30147 R ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,cmd
[1]+ Done sleep 3
end
$
$
$ dash -c 'echo start; (set -bm; echo $-; sleep 3 & ps -s '$$' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end'
start
bm
PID PPID PGID TPGID SID S CMD
30147 30146 30147 31245 30147 S -bash
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
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
31247 31246 31245 31245 30147 S sleep 3
31248 31246 31245 31245 30147 R ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,cmd
end # «« 3 seconds correctly elapsed before getting here
$
$
$ yash -c 'echo start; (set -bm; echo $-; sleep 3 & ps -s '$$' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end'
start
cmb
[1] + Running sleep 3
PID PPID PGID TPGID SID S CMD
30147 30146 30147 31252 30147 S -bash
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
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
31251 31250 31251 31252 30147 S sleep 3
31252 31250 31252 31252 30147 R ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,cmd
[1] + Done sleep 3
end
$
$
$ mksh -c 'echo start; (set -bm; echo $-; sleep 3 & ps -s '$$' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end'
start
mbhc
PID PPID PGID TPGID SID S CMD
30147 30146 30147 31253 30147 S -bash
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
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
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
31256 31254 31256 31253 30147 R ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,cmd
[1] + Done \sleep 3
end
$
$
$ ksh -c 'echo start; (set -bm; echo $-; sleep 3 & ps -s '$$' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end'
start
cbhmsB
PID PPID PGID TPGID SID S CMD
30147 30146 30147 31258 30147 S -bash
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,
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,
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,
31260 31258 31257 31258 30147 R ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,cmd
# —— 3 seconds correctly elapsed on this empty line (which is *not* by me) ——
[1]+ Stopped ksh -c 'echo start; (set -bm; echo $-; sleep 3 & ps -s '$$' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end'
$
$
$ fg # «« had to get rid of Stopped `ksh` from my login shell
ksh -c 'echo start; (set -bm; echo $-; sleep 3 & ps -s '$$' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end'
end
$
$
$ zsh -c 'echo start; (set -5m; echo $-; sleep 3 & ps -s '$$' -o pid,ppid,pgid,tpgid,sid,s,cmd; wait); echo end'
start
569Xm
PID PPID PGID TPGID SID S CMD
30147 30146 30147 31261 30147 S -bash
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,
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,
31263 31262 31263 31261 30147 S sleep 3
31264 31262 31264 31261 30147 R ps -s 30147 -o pid,ppid,pgid,tpgid,sid,s,cmd
end # «« 3 seconds correctly elapsed before getting here
$
根据ps
上述各种方法的 TPGID 值,在 shell 中,set -m
仅尊重bash
并yash
在子 shell 内提供完全工作的作业控制环境,而mksh
和zsh
则不这样做。
事实上,在额外的测试中,mksh
:
$ mksh -c 'echo start; (set -bm; echo $-; vim); echo end'
start
mbhc
Vim: Caught deadly signal HUP
Vim: Finished.
«« —— cursor stopped here. Then I hit Return ——
2R1: command not found
95: command not found
0c: command not found
$
尽管zsh
:
$ zsh -c 'echo start; (set -5m; echo $-; vim); echo end'
start
569Xm «« —— cursor stopped here. Then I `killall zsh` from another terminal ——
Terminated
$ Vim: Caught deadly signal HUP
Vim: Finished.
«« —— cursor stopped here. Then I hit Return ——
2R1: command not found
95: command not found
0c: command not found
$
当然vim
在我杀死它的父母之前处于停止状态zsh
:
$ ps -t 0 -o pid,ppid,pgid,tpgid,sid,s,cmd
PID PPID PGID TPGID SID S CMD
30147 30146 30147 31582 30147 S -bash
31582 30147 31582 31582 30147 S zsh -c echo start; (set -5m; echo $-; vim); echo end
31583 31582 31583 31582 30147 T vim
$
$ killall zsh
$
相反,bash
和都yash
正确地完成了所有操作:echo start
和$-
,启动完全可用的vim
(包括光标键和Ctrl+C),并echo end
在 Vim 退出后。
然而,正如我在前提中所说的,bash
当我给它提供相同的示例子外壳时,它崩溃了直接地从交互式 shell 中,显示错误的 TPGID,就像 和mksh
一样zsh
。在这次最后的测试中,yash
一切都按照我的预期进行。
最重要的是,zsh -c
并且mksh -c
(即非交互式)做了不是好吧,即使我把set -m
外部子壳只留vim
在里面。vim
只有当我把它拿走时,它们才在子壳上工作得很好set -m
。这意味着set -m
即使在脚本中也不能与这些 shell 一起工作(我后来实际上测试了它们,但它们失败了)。
我必须承认,这些测试显示得非常混乱,我也不确定上面使用的示例测试是否有一些错误的假设。因此我还尝试了一种更(据说)无害的:
#!/usr/bin/zsh
set -m
echo start
echo $-
tr '[a-z]' '[A-Z]'
echo end
确实如此不是工作:tr
立即停止,TPGID 继续zsh
进行。
它与 和yash
一起工作bash
(当然,它根本dash
不尊重),而不是在和上工作,尽管每个结果都与 不同。set -m
mksh
ksh
zsh
最后回到我的问题
在旁边本质上涉及的编程复杂性,作业控制真的意味着在子 shell 1中得到支持吗?也可能在脚本2内? (或者:我在这里没有看到什么?)
1 POSIX 的Shell执行环境似乎并没有禁止或强制执行
2摘自POSIX 描述set
:“set -m
选项 [...] 主要适用于交互式使用,而不是 shell 脚本应用程序。”