在bash中捕获信号但是没有完成当前正在运行的命令

时间:2017-08-09 12:10:40

标签: linux bash signals

我想捕捉一个信号(让我们专注于INT)而不是完成当前正在运行的命令,以便在运行信号处理程序后完成。

我们说我有以下脚本:

#!/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

当我在first sleep之后点击 Ctrl C 时,我会立即下降到second sleep,第二个sleep 1并完成脚本

当我在second sleep之后点击 Ctrl C 时(正在运行第二个sleep 1),我立即得到脚本终止。

如何确保此脚本在whole seconds中运行,即:sleep未终止?

1 个答案:

答案 0 :(得分:1)

您可以通过利用只有前台进程才会收到信号的事实来实现。因此,您可以在后台运行命令,在前台陷阱并wait直到命令退出。

但此外,由于wait will also exit on reception of a trapped signal

  

当Bash通过内置wait等待异步命令时,接收到已设置陷阱的信号将导致wait内置函数立即返回,退出状态大于128,紧接着执行陷阱。

我们必须在循环中等待,仅在低于(或等于)128 - 的退出状态时中止,假设命令永远不会退出,状态高于128 。如果此假设在您的情况下无效,则此解决方案将无效。

我们可以将所有这些包装在一个函数中,让我们称之为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退出trapwrap,因为这表示退出状态实际上来自刚刚完成的后台进程。最后,我们恢复陷阱并返回命令退出状态。)

然后,我们可以像这样使用#!/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

first sleep
^C^C^C^Csecond sleep
^C^C
1
Exit

运行时,您会得到预期的结果:

blockingStub = SomeGrpc
    .newBlockingStub(channel)
    .withDeadlineAfter(5, TimeUnit.SECONDS);