在脚本中打开作业控制以在 SIGCHLD 上设置陷阱可能很有用。手册中的作业控制部分说:
每当作业更改状态时,shell 都会立即学习。通常,bash 会等到它即将打印提示时才会报告作业状态的变化,以免中断任何其他输出。如果启用了 set 内置命令的 -b 选项,bash 会立即报告此类更改。 SIGCHLD 上的任何陷阱都会针对每个退出的子节点执行。
(重点是我的)
以下面的脚本为例:
dualbus@debian:~$ cat children.bash
#!/bin/bash
set -m
count=0 limit=3
trap 'counter && { job & }' CHLD
job() {
local amount=$((RANDOM % 8))
echo "sleeping $amount seconds"
sleep "$amount"
}
counter() {
((count++ < limit))
}
counter && { job & }
wait
dualbus@debian:~$ chmod +x children.bash
dualbus@debian:~$ ./children.bash
sleeping 6 seconds
sleeping 0 seconds
sleeping 7 seconds
注意:从 bash 4.3 开始,CHLD 捕获似乎已被破坏
但是,在 bash 4.3 中,您可以使用 'wait -n' 来实现相同的目的:
dualbus@debian:~$ cat waitn.bash
#!/home/dualbus/local/bin/bash
count=0 limit=3
trap 'kill "$pid"; exit' INT
job() {
local amount=$((RANDOM % 8))
echo "sleeping $amount seconds"
sleep "$amount"
}
for ((i=0; i<limit; i++)); do
((i>0)) && wait -n; job & pid=$!
done
dualbus@debian:~$ chmod +x waitn.bash
dualbus@debian:~$ ./waitn.bash
sleeping 3 seconds
sleeping 0 seconds
sleeping 5 seconds
您可能会争辩说还有其他方法可以以更便携的方式执行此操作,即无需 CHLD 或等待 -n:
dualbus@debian:~$ cat portable.sh
#!/bin/sh
count=0 limit=3
trap 'counter && { brand; job & }; wait' USR1
unset RANDOM; rseed=123459876$$
brand() {
[ "$rseed" -eq 0 ] && rseed=123459876
h=$((rseed / 127773))
l=$((rseed % 127773))
rseed=$((16807 * l - 2836 * h))
RANDOM=$((rseed & 32767))
}
job() {
amount=$((RANDOM % 8))
echo "sleeping $amount seconds"
sleep "$amount"
kill -USR1 "$$"
}
counter() {
[ "$count" -lt "$limit" ]; ret=$?
count=$((count+1))
return "$ret"
}
counter && { brand; job & }
wait
dualbus@debian:~$ chmod +x portable.sh
dualbus@debian:~$ ./portable.sh
sleeping 2 seconds
sleeping 5 seconds
sleeping 6 seconds
因此,总而言之,set -m在脚本中并没有那么有用,因为它为脚本带来的唯一有趣的特性就是能够使用 SIGCHLD。还有其他方法可以实现同样的事情,或者更短(等待 -n)或更便携(自己发送信号)。