在bash中捕获信号,但未完成当前正在运行的命令 [英] Catch signal in bash but don't finish currently running command
问题描述
我想捕获一个信号(让我们专注于 INT
),并且不完成当前正在运行的命令,以便在运行信号处理程序之后将其完成.
I would like to catch a signal ( let's focus on INT
) and not finish the currently running command so that it finishes after running signal handler.
假设我有以下脚本:
#!/bin/bash
READY=0
ctrl_c(){
READY=1
}
trap ctrl_c INT
while true; do
echo first sleep
sleep 1
echo second sleep
sleep 1
if [ $READY -ne 0 ] ; then
echo -e "\n$READY"
echo Exit
exit
fi
done
当我在第一次睡眠
之后按 Ctrl C 时,我立即下降到第二次睡眠
,第二次 sleep 1
并完成脚本.
When I hit CtrlC after first sleep
then I get immediately dropped down to second sleep
, second sleep 1
and script finished.
当我在第二次睡眠
之后(运行第二个 sleep 1
时)按下 Ctrl C 时,我立即终止脚本.
When I hit CtrlC after second sleep
(while second sleep 1
is being run) then I get immediately script termination.
如何确保此脚本在整秒
中运行,即: sleep
s不终止?
How can I ensure that this script runs in whole seconds
that is: sleep
s are not terminated?
推荐答案
您可以利用只有前台进程才能接收信号的事实来做到这一点.因此,您可以在后台运行您的命令,在前台捕获并等待
直到命令退出.
You can do it by exploiting the fact that only the foreground process will receive the signal. So, you can run your command in background, trap in foreground and wait
until the command exits.
但是,另外,由于 wait
也会在收到捕获的信号时退出:
当Bash通过内置的
wait
等待异步命令时,接收到已设置陷阱的信号将导致内置的wait
立即返回退出状态大于128,然后立即执行陷阱.
When Bash is waiting for an asynchronous command via the
wait
builtin, the reception of a signal for which a trap has been set will cause thewait
builtin to return immediately with an exit status greater than 128, immediately after which the trap is executed.
我们将不得不循环等待,仅在低于(或等于)128的退出状态时中止– 假设命令永远不会以高于128的状态退出.如果此假设在您的情况下无效,则此解决方案将不起作用.
we'll have to wait in a loop, aborting only on exit status below (or equal to) 128 -- assuming the command will never exit with status above 128. If this assumption is not valid in your case, then this solution won't work.
我们可以将所有这些包装在一个函数中,我们称其为 trapwrap
:
We can wrap all this in a function, let's call it trapwrap
:
trapwrap() {
declare -i pid status=255
# set the trap for the foreground process
trap ctrl_c INT
# run the command in background
"$@" & pid=$!
# wait until bg command finishes, handling interruptions by trapped signals
while (( status > 128 )); do
wait $pid
status=$?
done
# restore the trap
trap - INT
# return the command exit status
return $status
}
(解释:首先我们将pid
和status
声明为整数,这样我们以后就不必对它们进行转义.为SIGINT设置陷阱后
到先前定义的用户函数 ctrl_c
,我们在后台运行用户提供的命令,并将其 PID
存储在 pid
中. wait
在无限循环中完成 pid
的完成,因为 wait
也会在捕获的 SIGINT
上中断,在这种情况下它以状态> 128
退出.我们循环运行,直到 wait
以< = 128
退出,因为这表示退出状态实际上来自后台进程刚刚完成.最后,我们恢复陷阱并返回命令退出状态.)
(Explanation: first we declare pid
and status
as integers, so we don't have to escape them later on. After setting the trap for SIGINT
to a previously defined user function ctrl_c
, we run the user-supplied command in background and store its PID
in pid
. We wait
for pid
's completion in an infinite loop because wait
will also break on trapped SIGINT
, in which case it exits with status >128
. We loop until wait
exits with <=128
, as that signifies the exit status is actually coming from the background process which just finished. Finally, we restore the trap and return the command exit status.)
然后,我们可以像这样使用 trapwrap
:
Then, we can use the trapwrap
like this:
#!/bin/bash
READY=0
ctrl_c() {
READY=1
}
while true; do
echo first sleep
trapwrap sleep 1
echo second sleep
trapwrap sleep 1
if [ $READY -ne 0 ] ; then
echo -e "\n$READY"
echo Exit
exit
fi
done
运行时,您将获得预期的结果:
When run, you get the expected result:
first sleep
^C^C^C^Csecond sleep
^C^C
1
Exit
这篇关于在bash中捕获信号,但未完成当前正在运行的命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!