Ctrl + C时如何在shell脚本中杀死并等待后台进程完成?


24

我正在尝试设置一个shell脚本,使其运行后台进程,当我执行Ctrlc该shell脚本时,它将杀死子级,然后退出。

我设法提出的最好的办法就是这个。看来,在kill 0 -INT等待发生之前也会杀死脚本,因此shell脚本在子进程完成之前就死掉了。

关于如何使此Shell脚本等待孩子在发送后死亡的任何想法INT

#!/bin/bash
trap 'killall' INT

killall() {
    echo "**** Shutting down... ****"
    kill 0 -INT
    wait # Why doesn't this wait??
    echo DONE
}

process1 &
process2 &
process3 &

cat # wait forever


我通过搜索“ shell Intercept Kill Switch”找到了这个问题,感谢共享陷阱命令,当应用程序无法通过关闭GUI按钮正确退出时,使用Ctrl + C退出应用程序后执行命令非常有用。
baptx

Answers:


22

您的kill命令是向后的。

像许多UNIX命令一样,以减号开头的选项必须先于其他参数。

如果你写

kill -INT 0

它把-INT视为一个选项,然后发送SIGINT00是一个特殊数字,表示当前进程组中的所有进程)。

但是如果你写

kill 0 -INT

它会看到0,决定没有更多选项,因此SIGTERM默认使用。并将当前进程组,因为如果你做了同样的

kill -TERM 0 -INT    

(它还会尝试发送SIGTERM-INT,这会导致语法错误,但它先发送SIGTERM0,再也不会走那么远了。)

因此,您的主脚本SIGTERM在运行waitand 之前先获取了一个echo DONE

trap 'echo got SIGTERM' TERM

在顶部,紧随其后

trap 'killall' INT

并再次运行以证明这一点。

正如Stephane Chazelas所指出的,默认情况下,您有背景的孩子(process1,等)将被忽略SIGINT

无论如何,我认为发送SIGTERM会更有意义。

最后,我不确定是否一定kill -process group要先去给孩子们。关闭时忽略信号可能是一个好主意。

所以试试这个:

#!/bin/bash
trap 'killall' INT

killall() {
    trap '' INT TERM     # ignore INT and TERM while shutting down
    echo "**** Shutting down... ****"     # added double quotes
    kill -TERM 0         # fixed order, send TERM not INT
    wait
    echo DONE
}

./process1 &
./process2 &
./process3 &

cat # wait forever

谢谢,这有效!这似乎是有问题的。
2012年

9

不幸的是,shell将后台启动的命令设置为忽略SIGINT,更糟糕的是,它们不能使用忽略它trap。否则,您要做的就是

(trap - INT; exec process1) &
(trap - INT; exec process2) &
trap '' INT
wait

因为当您按Ctrl-C时process1和process2会获得SIGINT,因为它们属于同一进程组,而该进程组是终端的前台进程组。

上面的代码将与不符合POSIX的pdksh和zsh一起使用。

对于其他shell,您将不得不使用其他方法来还原SIGINT的默认处理程序,例如:

perl -e '$SIG{INT}=DEFAULT; exec "process1"' &

或使用其他信号(例如SIGTERM)。


2

对于那些只想终止一个进程并等待其终止但又不是无限期的用户

每种信号类型最多等待60秒。

警告:此答案与捕获终止信号并进行分派无关。

# close_app_sub GREP_STATEMENT SIGNAL DURATION_SEC
# GREP_STATEMENT must not match itself!
close_app_sub() {
    APP_PID=$(ps -x | grep "$1" | grep -oP '^\s*\K[0-9]+' --color=never)
    if [ ! -z "$APP_PID" ]; then
        echo "App is open. Trying to close app (SIGNAL $2). Max $3sec."
        kill $2 "$APP_PID"
        WAIT_LOOP=0
        while ps -p "$APP_PID" > /dev/null 2>&1; do
            sleep 1
            WAIT_LOOP=$((WAIT_LOOP+1))
            if [ "$WAIT_LOOP" = "$3" ]; then
                break
            fi
        done
    fi
    APP_PID=$(ps -x | grep "$1" | grep -oP '^\s*\K[0-9]+' --color=never)
    if [ -z "$APP_PID" ]; then return 0; else return "$APP_PID"; fi
}

close_app() {
    close_app_sub "$1" "-HUP" "60"
    close_app_sub "$1" "-TERM" "60"
    close_app_sub "$1" "-SIGINT" "60"
    close_app_sub "$1" "-KILL" "60"
    return $?
}

close_app "[f]irefox"

它选择要按名称或参数杀死的应用程序。保留应用程序名称首字母的方括号,以避免与grep本身匹配。

进行一些更改后,您可以直接使用PID或更简单的PID pidof process_name来代替ps语句。

代码详细信息:最终grep是获取没有尾随空格的PID。


1

如果要管理一些后台进程,为什么不使用bash作业控制功能?

$ gedit &
[1] 2581
$ emacs &
[2] 2594
$ jobs
[1]-  Running                 gedit &
[2]+  Running                 emacs &
$ jobs -p
2581
2594
$ kill -2 `jobs -p`
$ jobs
[1]-  Interrupt               gedit
[2]+  Done                    emacs

0

因此,我也对此进行了修改。在Bash中,您可以定义函数并用它们做一些花哨的事情。我和我的同事使用terminator,因此对于批处理执行,如果我们要查看输出(通常不如tmux制表符优雅,但可以使用更像GUI的界面,哈哈),通常会产生一堆终结器窗口。

这是我想到的设置,以及可以运行的东西的示例:

#!/bin/bash
custom()
{
    terun 'something cool' 'yes'
    run 'xclock'
    terun 'echoes' 'echo Hello; echo Goodbye; read'
    func()
    {
        local i=0
        while :
        do
            echo "Iter $i"
            let i+=1
            sleep 0.25
        done
    }
    export -f func
    terun 'custom func' 'func'
}

# [ Setup ]
run()
{
    "$@" &
    # Give process some time to start up so windows are sequential
    sleep 0.05
}
terun()
{
    run terminator -T "$1" -e "$2"
}
finish()
{
    procs="$(jobs -p)"
    echo "Kill: $procs"
    # Ignore process that are already dead
    kill $procs 2> /dev/null
}
trap 'finish' 2

custom

echo 'Press <Ctrl+C> to kill...'
wait

运行它

$ ./program_spawner.sh 
Press <Ctrl+C> to kill...
^CKill:  9835
9840
9844
9850
$ 

编辑 @Maxim,刚看到您的建议,这使它变得更加简单!谢谢!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.