捕获用户信号并继续执行
trap user signal and continue executing
#!/bin/sh
trap 'echo yo2 > $fifo' USR1
out(){ while read -r line; do echo $line; done; }
fifo=~/fifo
[ -e $fifo ] && rm $fifo
mkfifo fifo
tail -f > $fifo &
while :; do
echo yo1 > $fifo
sleep 1
done &
out < $fifo
我试图捕获 USR1 信号,以便回显到我在脚本中保持打开状态的 FIFO,但在收到信号后,FIFO 关闭,脚本终止。
当主进程得到SIGUSR1时,它正处于读取的中间(inside function out)。很少有事情会发生:
* 挂起的读被挂起
* 陷阱被激活(将 yo2 附加到 fifo)
* 恢复读取,并立即失败并返回错误代码
* while循环in out终止,脚本退出。
您可能需要考虑一个不同的解决方案,其中 'out' 方法是后台的,'main' 脚本将只处理信号请求,同时等待子进程完成
替换线路,out
out < $fifo &
# Loop forever. Add exit condition.
while true ; do wait ; done
简答
当 shell 处理带有 trap
的信号时,任何当前挂起的 read
都将失败。这就是脚本提前终止的原因。
要解决此问题,请在处理程序中设置一个标志。然后当 read
失败时,检查标志。如果设置了标志,那么您知道 read
由于信号而不是文件结束而失败,因此您应该继续执行循环。
发生了什么事?
首先,让我们稍微简化脚本并添加一些打印输出以查看发生了什么。这里是 trap-fifo1.sh
:
#!/bin/sh
echo "my PID: $$"
trap handler USR1
handler() {
echo "in 'handler'"
echo caught_user1 > fifo
echo "handler complete"
}
out() {
echo "in 'out'"
while read -r line; do
echo $line
done
echo "loop terminated"
}
echo "about to invoke 'out'"
out < fifo
echo "at end of script"
要查看问题清单,我将 运行 在三个不同的终端中执行三个命令。每个终端会话用一列表示,纵轴为全球时间:
Terminal 1 Terminal 2 Terminal 3
--------------- ------------- ----------------
$ ./trap-fifo1.sh
my PID: 42436
about to invoke 'out'
$ cat > fifo
in 'out'
hi
hi
$ kill -s SIGUSR1 42436
in 'handler'
handler complete
loop terminated
at end of script
当我们发送 SIGUSR1
时循环停止。这是因为在接收和处理信号时挂起的 read
失败。 (旁白:我努力为这种凭经验观察到的行为找到权威参考。我找到的最好的是 https://ss64.com/bash/trap.html but I don't know where that text comes from. I could not find it in either POSIX nor the bash manual。)
我们该如何解决?
问题是 read
中的歧义,我们不知道失败是因为 trap
还是因为文件结尾。所以我们会在handler
中设置一个flag。这里是 trap-fifo2.sh
:
#!/bin/sh
echo "my PID: $$"
trap handler USR1
handler_invoked=false
handler() {
echo "in 'handler'"
echo caught_user1 > fifo
handler_invoked=true
echo "handler complete"
}
out() {
echo "in 'out'"
while true; do
if read -r line; then
echo $line
elif $handler_invoked; then
echo "flag set, continuing"
handler_invoked=false
else
echo "flag unset, stopping"
break
fi
done
echo "after while loop"
}
echo "about to invoke 'out'"
out < fifo
echo "at end of script"
现在让我们看看实际修复:
Terminal 1 Terminal 2 Terminal 3
--------------- ------------- ----------------
$ ./trap-fifo2.sh
my PID: 42562
about to invoke 'out'
$ cat > fifo
in 'out'
hello
hello
$ kill -s SIGUSR1 42562
in 'handler'
handler complete
flag set, continuing
caught_user1
again
again
$ kill -s SIGUSR1 42562
in 'handler'
handler complete
flag set, continuing
caught_user1
(Ctrl+D)
flag unset, stopping
after while loop
at end of script
从那里我们只需要通过添加标志并在循环中检查它来调整原始脚本,这应该很简单。
#!/bin/sh
trap 'echo yo2 > $fifo' USR1
out(){ while read -r line; do echo $line; done; }
fifo=~/fifo
[ -e $fifo ] && rm $fifo
mkfifo fifo
tail -f > $fifo &
while :; do
echo yo1 > $fifo
sleep 1
done &
out < $fifo
我试图捕获 USR1 信号,以便回显到我在脚本中保持打开状态的 FIFO,但在收到信号后,FIFO 关闭,脚本终止。
当主进程得到SIGUSR1时,它正处于读取的中间(inside function out)。很少有事情会发生: * 挂起的读被挂起 * 陷阱被激活(将 yo2 附加到 fifo) * 恢复读取,并立即失败并返回错误代码 * while循环in out终止,脚本退出。
您可能需要考虑一个不同的解决方案,其中 'out' 方法是后台的,'main' 脚本将只处理信号请求,同时等待子进程完成
替换线路,out
out < $fifo &
# Loop forever. Add exit condition.
while true ; do wait ; done
简答
当 shell 处理带有 trap
的信号时,任何当前挂起的 read
都将失败。这就是脚本提前终止的原因。
要解决此问题,请在处理程序中设置一个标志。然后当 read
失败时,检查标志。如果设置了标志,那么您知道 read
由于信号而不是文件结束而失败,因此您应该继续执行循环。
发生了什么事?
首先,让我们稍微简化脚本并添加一些打印输出以查看发生了什么。这里是 trap-fifo1.sh
:
#!/bin/sh
echo "my PID: $$"
trap handler USR1
handler() {
echo "in 'handler'"
echo caught_user1 > fifo
echo "handler complete"
}
out() {
echo "in 'out'"
while read -r line; do
echo $line
done
echo "loop terminated"
}
echo "about to invoke 'out'"
out < fifo
echo "at end of script"
要查看问题清单,我将 运行 在三个不同的终端中执行三个命令。每个终端会话用一列表示,纵轴为全球时间:
Terminal 1 Terminal 2 Terminal 3
--------------- ------------- ----------------
$ ./trap-fifo1.sh
my PID: 42436
about to invoke 'out'
$ cat > fifo
in 'out'
hi
hi
$ kill -s SIGUSR1 42436
in 'handler'
handler complete
loop terminated
at end of script
当我们发送 SIGUSR1
时循环停止。这是因为在接收和处理信号时挂起的 read
失败。 (旁白:我努力为这种凭经验观察到的行为找到权威参考。我找到的最好的是 https://ss64.com/bash/trap.html but I don't know where that text comes from. I could not find it in either POSIX nor the bash manual。)
我们该如何解决?
问题是 read
中的歧义,我们不知道失败是因为 trap
还是因为文件结尾。所以我们会在handler
中设置一个flag。这里是 trap-fifo2.sh
:
#!/bin/sh
echo "my PID: $$"
trap handler USR1
handler_invoked=false
handler() {
echo "in 'handler'"
echo caught_user1 > fifo
handler_invoked=true
echo "handler complete"
}
out() {
echo "in 'out'"
while true; do
if read -r line; then
echo $line
elif $handler_invoked; then
echo "flag set, continuing"
handler_invoked=false
else
echo "flag unset, stopping"
break
fi
done
echo "after while loop"
}
echo "about to invoke 'out'"
out < fifo
echo "at end of script"
现在让我们看看实际修复:
Terminal 1 Terminal 2 Terminal 3
--------------- ------------- ----------------
$ ./trap-fifo2.sh
my PID: 42562
about to invoke 'out'
$ cat > fifo
in 'out'
hello
hello
$ kill -s SIGUSR1 42562
in 'handler'
handler complete
flag set, continuing
caught_user1
again
again
$ kill -s SIGUSR1 42562
in 'handler'
handler complete
flag set, continuing
caught_user1
(Ctrl+D)
flag unset, stopping
after while loop
at end of script
从那里我们只需要通过添加标志并在循环中检查它来调整原始脚本,这应该很简单。