在bash中捕获信号,但未完成当前正在运行的命令 [英] Catch signal in bash but don't finish currently running command

查看:48
本文介绍了在bash中捕获信号,但未完成当前正在运行的命令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想捕获一个信号(让我们专注于 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: sleeps 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 the wait 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
}

(解释:首先我们将pidstatus声明为整数,这样我们以后就不必对它们进行转义.为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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆