Bash脚本捕获信号,但随后等待进程终止



目前我正在编写这样的bash脚本:

foo(){
while true
do
sleep 10
done
}
bar(){
while true
do
sleep 20
done
}
foo &
bar &
wait

(我知道这样的脚本没有意义,它只是关于结构(

现在我想添加trap -- <doSomething> RTMIN+1的信号处理。这在一开始是有效的。当脚本接收到rtmin+1信号时,它确实做了一些事情,但之后它就存在了(退出代码为163,这是正在发送的信号的编号(。

这不是我想要的行为。我希望在收到信号后,脚本继续等待进程(在本例中是两个函数(终止(当然,在本例中将不会发生这种情况,但脚本应该等待(。

我尝试在接收信号时将; wait添加到应该做的事情中,但这没有帮助(或者我做错了什么(。

有人知道如何实现期望的行为吗?

提前感谢并致以最良好的祝愿。

编辑:也许一个更精确的例子有帮助:

clock(){
local prefix=C
local interval=1
while true
do
printf "${prefix} $(date '+%d.%m %H:%M:%S')n"
sleep $interval
done
}
volume(){
prefix=V
volstat="$(amixer get Master 2>/dev/null)"
echo "$volstat" | grep "[off]" >/dev/null && icon="" #alternative: deaf:  mute: 
vol=$(echo "$volstat" | grep -o "[[0-9]+%]" | sed "s/[^0-9]*//g;1q")
if [ -z "$icon" ] ; then
if [ "$vol" -gt "50" ]; then
icon=""
#elif [ "$vol" -gt "30" ]; then
#       icon=""
else
icon=""
fi
fi
printf "${prefix}%s %3s%%n" "$icon" "$vol"
}
clock &
volume &
trap -- "volume" RTMIN+2
wait

现在RTMIN+2信号应该重新运行volume函数,但clock过程不应该中断。(到目前为止,整个脚本(包括所有子流程(在接收到信号时终止(

当在没有操作数的情况下调用时,wait会以0退出;这意味着调用shell已知的所有进程ID都已终止,或者值大于128;这意味着已经设置了陷阱的信号被接收。因此,循环直到wait0一起退出就足以在接收到信号后保持脚本的有效性。您也可以在循环中检查它的退出状态是否小于128,但我认为这没有必要。

但是,如果使用pkill发送这些信号,则在后台启动的作业也会接收到这些信号,因为子进程从其父进程继承进程名称,而不是自定义信号处理程序,并且pkill会向名称匹配的所有进程发信号。你也需要处理那个案子。

一个最小的工作示例是:

#! /bin/bash -
# ignore RTMIN+1 and RTMIN+2 temporarily
# to prevent pkill from killing jobs
trap '' RTMIN+1 RTMIN+2
# start jobs
sleep 20 && echo slept for 20 seconds &
sleep 30 && echo slept for 30 seconds &
# set traps
trap 'echo received rtmin+1' RTMIN+1
trap 'echo received rtmin+2' RTMIN+2
# wait for jobs to terminate
until wait; do
echo still waiting
done

同样值得注意的是,在开始作业之前忽略RTMIN+1RTMIN+2可以防止从它们下降的外壳捕获/重新设置这些信号。如果这是一个问题,你可以像F.Hauri建议的那样,在作业中设置一个空陷阱;或者您可以完全放弃忽略,使用pkill-o选项将信号发送到最旧的匹配进程。

参考文献:

  • 外壳命令语言§信号和错误处理
  • wait规范§退出状态

相关:

  • USR1信号后可靠终止睡眠过程

某些已重写

为了避免一些无用的叉子。

clock(){  local prefix=C interval=2
trap : RTMIN{,+{{,1}{1,2,3,4,5},6,7,8,9,10}}
while :;do
printf "%s: %(%d.%m %H:%M:%S)Tn" $prefix -1
sleep $interval
done
}
volume(){  local prefix=V vol=() field playback val foo
while IFS=':[]' read field playback val foo;do
[ "$playback" ] && [ -z "${playback//*Playback*}" ] && [ "$val" ] &&
vol+=(${val%%})
done < <(amixer get Master)
suffix='%%'
if [ "$vol" = "off" ] ;then
icon="" #alternative: deaf:  mute: 
suffix=''
elif (( vol > 50 )) ;then  icon=""
elif (( vol > 30 )) ;then  icon=""
else                       icon=""
fi
printf -v values "%3s$suffix " ${vol[@]}
printf "%s%s %sn" $prefix "$icon" "$values"
}
clock & volume &
trap volume RTMIN+2
trap : RTMIN{,+{{,1}{1,3,4,5},6,7,8,9,10,12}}
echo -e "To get status, run:n  kill -RTMIN+2 $$"
while :;do wait ;done

关于我上次关于立体声错误的评论,有一个音量函数适用于立体声、单声道甚至四声道:

volume(){
local prefix=V vol=() field playback val foo
local -i overallvol=0
while IFS=':[]' read field playback val foo ;do
[ "$playback" ] && [ -z "${playback//*Playback*}" ] && [ "$val" ] && {
vol+=($val)
val=${val%%}
overallvol+=${val//off/0}
}
done < <(
amixer get Master
)
overallvol=$overallvol/${#vol[@]}
if (( overallvol == 0 )) ;then
icon=""
elif (( overallvol > 50 )) ;then
icon=""
elif (( overallvol > 30 )) ;then
icon=""
else
icon=""
fi
printf "%s%s %sn" $prefix "$icon" "${vol[*]}"
}

甚至:

volume(){
local prefix=V vol=() field playback val foo icons=(⏻ ¼ ¼ ¼ ½ ½ ¾ ¾ ¾ ¾ ¾)
local -i overallvol=0
while IFS=':[]' read field playback val foo ;do
[ "$playback" ] && [ -z "${playback//*Playback*}" ] && [ "$val" ] && {
vol+=($val)
val=${val%%}
overallvol+=${val//off/0}
}
done < <(
amixer get Master
)
overallvol=$overallvol/${#vol[@]}
printf "%s%s %sn" $prefix "${icons[(9+overall)/10]}" "${vol[*]}"

一些解释

关于volume()函数中的无用叉子

我在那里发布了一些改进工作的想法,减少资源消耗,并做同样的工作,选择图标作为当前音量集的函数。

关于while :;do wait;done循环

由于所请求的示例代表了在后台子函数中的无限循环,所以主脚本使用相同的无限循环。

但由于问题标题代表等待进程终止,我不得不同意oguz-ismail的评论。

事实上,最后一行最好写:

until wait;do :;done

有关wait命令如何工作和良好实践的更多信息,请查看good-oguz-ismail的答案

最新更新