6

ある時点で実行時間の長いサブプロセスを同期的に実行する bash スクリプト プロセスがあります。そのサブプロセスの実行中に、スクリプトの終了を要求する信号が bash スクリプト プロセスに直接送信されます。そのシグナルを傍受し、サブプロセスを終了してから bash プロセスを終了する方法はありますか?

どうやら、bash のシグナル処理は同期呼び出しを中断することはありませんか?

終了シグナルが bash プロセスに送信されるという事実を制御できません。シグナルが子プロセスに伝播できれば、それも私の問題を解決します。

前もって感謝します、ブロス

4

3 に答える 3

2

これを処理するために私が書いた bash ユーティリティ関数を次に示します。便利で堅牢であることが証明されています。お役に立てば幸いです。

# Run a command in a way that can be interrupted by a signal (eg SIGTERM)
#
# When bash receives a SIGTERM it normally simply exits. If it's executing a subprocess
# that subprocess isn't signaled. (Typically that's not a problem for interactive shells
# because the entire Process Group gets sent the signal.)
#
# When running a script it's sometimes useful for the script to propagate a SIGTERM
# to the command that was running. We can do that by using the trap builtin to catch
# the signal. But it's a little tricky, per the bash manual:
#
#    If bash is waiting for a command to complete and receives a signal for
#    which a trap has been set, the trap will not be executed until the
#    command completes.
#
# so a script executing a long-running command with a signal trap set won't
# notice the signal until later. There's a way around that though...
#
#    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.
#
# Usage:
#
#   interruptable [options] command [args]
#
# Options:
#   --killall - put the child into a process group (via setsid)
#               and send the SIGTERM to the process group
#   --debug   - print a message including pid of the child
#
# Usage examples:
#
#   interruptable sleep 3600
#
# If not interrupted, the exit status of the specified command is returned.
# If interrupted, the specified command is sent a SIGTERM and the current
# shell exits with a status of 143.

interruptable() {

    # handle options
    local setsid=""
    local debug=false
    while true; do
        case "${1:-}" in
            --killall)      setsid=setsid; shift ;;
            --debug)        debug=true; shift ;;
            --*)            echo "Invalid option: $1" 1>&2; exit 1;;
            *)              break;; # no more options
        esac
    done

    # start the specified command
    $setsid "$@" &
    local child_pid=$!

    # arrange to propagate a signal to the child process
    trap '
        exec 1>&2
        set +e
        trap "" SIGPIPE # ensure a possible sigpipe from the echo does not prevent the kill
        echo "${BASH_SOURCE[0]} caught SIGTERM while executing $* (pid $child_pid), sending SIGTERM to it"
        # (race) child may have exited in which case kill will report an error
        # if setsid is used then prefix the pid with a "-" to indicate that the signal
        # should be sent to the entire process group
        kill ${setsid:+-}$child_pid
        exit 143
    ' SIGTERM
    # ensure that the trap doesn't persist after we return
    trap 'trap - SIGTERM' RETURN

    $debug && echo "interruptable wait (child $child_pid, self $$) for: $*"

    # An error status from the child process will trigger an exception (via set -e)
    # here unless the caller is checking the return status
    wait $child_pid # last command, so status of waited for command is returned
}
于 2015-12-11T14:36:03.353 に答える
0

はい、trapコマンドで信号を傍受できます。以下の例を参照してください。

#!/bin/bash
function wrap {
local flag=0
trap "flag=1" SIGINT SIGTERM
xeyes &
subppid=$!

while : 
    do
        if [ $flag -ne 0 ] ; then
            kill $subppid
            break 
        fi
            sleep 1        
    done
}
flag=0
trap "flag=1" SIGINT SIGTERM
wrap &
wrappid=$!
while :                 # This is the same as "while true".
    do
        if [ $flag -ne 0 ] ; then
            kill $wrappid
            break
        fi
        sleep 1        # This script is not really doing anything.
done
echo 'end'

trap基本的には、"" の間のコマンドを実行することです。したがって、ここでは main 関数が以下の while ループにあります。反復ごとに、スクリプトはフラグが設定されているかどうかをチェックし、設定されていない場合は 1 秒間スリープします。その前に、子プロセスの pid を$!. SIGINTorがキャッチされると、トラップはコマンドを発行しますSIGTERM(他のシグナルについては、killマニュアルを参照してください)。

ラッパー関数は、メイン関数と同じことを行います。さらに、実際のsubprocess関数を呼び出します (この場合、サブプロセスは ですxeyes)。ラッパー関数SIGTERMがメイン関数からシグナルを受信すると (メイン関数もシグナルの 1 つをキャッチした場合)、ラッパー関数は実際にサブプロセスを強制終了する前にクリーンアップできます。その後、while ループから抜け出し、ラッパー関数を終了します。次に、メイン関数も中断して出力し'end'ます。

編集:これを正しく理解していることを願っています。実行を余儀なくされていますxeyes &。次に、手順は次のようになります(ターミナルで):

xeyes &
subpid=$!
trap "kill $subpid && exit " SIGINT SIGTERM
.... other stuff
.... more stuff
^C #TERMINATE - this firstly kills xeyes and then exits the terminal
于 2012-10-12T12:36:33.670 に答える