bash 如何从bash脚本终止Cygwin bash中脚本的进程树

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/523878/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 20:40:32  来源:igfitidea点击:

How to terminate script's process tree in Cygwin bash from bash script

bashprocessscriptingcygwinterminate

提问by Barry Kelly

I have a Cygwin bash script that I need to watch and terminate under certain conditions - specifically, after a certain file has been created. I'm having difficulty figuring out how exactly to terminate the script with the same level of completeness that Ctrl+C does, however.

我有一个 Cygwin bash 脚本,我需要在某些条件下观察并终止它 - 特别是在创建某个文件之后。但是,我很难弄清楚如何以与 Ctrl+C 相同的完整性级别来终止脚本。

Here's a simple script (called test1) that does little more than wait around to be terminated.

这是一个简单的脚本(称为test1),它所做的只是等待被终止。

#!/bin/bash

test -f kill_me && rm kill_me

touch kill_me
tail -f kill_me

If this script is run in the foreground, Ctrl+C will terminate both the tailand the script itself. If the script is run in the background, a kill %1(assuming it is job 1) will also terminate both tailand the script.

如果此脚本在前台运行,Ctrl+C 将终止tail脚本和脚本本身。如果脚本在后台运行,a kill %1(假设它是作业 1)也将终止tail脚本和脚本。

However, when I try to do the same thing from a script, I'm finding that only the bashprocess running the script is terminated, while tailhangs around disconnected from its parent. Here's one way I tried (test2):

但是,当我尝试从脚本执行相同的操作时,我发现只有bash运行脚本的进程被终止,而tail与其父进程断开连接。这是我尝试的一种方法 ( test2):

#!/bin/bash

test -f kill_me && rm kill_me

(
    touch kill_me
    tail -f kill_me
) &

while true; do
    sleep 1
    test -f kill_me && {
        kill %1
        exit
    }
done

If this is run, the bash subshell running in the background is terminated OK, but tailstill hangs around.

如果这样运行,后台运行的 bash 子shell 就可以终止了,但tail仍然挂在那里。

If I use an explicitly separate script, like this, it still doesn't work (test3):

如果我使用显式单独的脚本,像这样,它仍然不起作用 ( test3):

#!/bin/bash

test -f kill_me && rm kill_me

# assuming test1 above is included in the same directory
./test1 &

while true; do
    sleep 1
    test -f kill_me && {
        kill %1
        exit
    }
done

tailis still hanging around after this script is run.

tail运行此脚本后仍在徘徊。

In my actual case, the process creating files is not particularly instrumentable, so I can't get it to terminate of its own accord; by finding out when it has created a particular file, however, I can at that point know that it's OK to terminate it. Unfortunately, I can't use a simple killallor equivalent, as there may be multiple instances running, and I only want to kill the specific instance.

在我的实际情况中,创建文件的过程不是特别有用,所以我无法让它自行终止;但是,通过找出它何时创建了特定文件,我可以知道可以终止它。不幸的是,我不能使用简单的killall或等效的,因为可能有多个实例在运行,而我只想杀死特定的实例。

回答by andrewdotn

/bin/kill (the program, not the bash builtin) interprets a negativePID as “kill the process group” which will get all the children too.

/bin/kill(程序,而不是内置的 bash)将PID解释为“杀死进程组”,这也将获得所有子进程。

Changing

改变

kill %1

to

/bin/kill -- -$$

works for me.

对我来说有效。

回答by Barry Kelly

Adam's link put me in a direction that will solve the problem, albeit not without some minor caveats.

Adam 的链接让我找到了解决问题的方向,尽管有一些小问题。

The script doesn't work unmodified under Cygwin, so I rewrote it, and with a couple more options. Here's my version:

该脚本在 Cygwin 下未经修改无法运行,因此我重新编写了它,并提供了更多选项。这是我的版本:

#!/bin/bash

function usage
{
    echo "usage: $(basename 
#!/bin/bash
# Author: Sunil Alankar

##
# recursive kill. kills the process tree down from the specified pid
#

# foreach child of pid, recursive call dokill
dokill() {
    local pid=
    local itsparent=""
    local aprocess=""
    local x=""
    # next line is a single line
    for x in `/bin/ps -f | sed -e '/UID/d;s/[a-zA-Z0-9_-]\{1,\}
\{1,\}\([0-9]\{1,\}\) \{1,\}\([0-9]\{1,\}\) .*/ /g'`
    do
        if [ "$aprocess" = "" ]; then
            aprocess=$x
            itsparent=""
            continue
        else
            itsparent=$x
            if [ "$itsparent" = "$pid" ]; then
                dokill $aprocess
            fi
            aprocess=""
        fi
    done
    echo "killing "
    kill -9  > /dev/null 2>&1
}

case $# in
1) PID=
        ;;
*) echo "usage: rekill <top pid to kill>";
        exit 1;
        ;;
esac

dokill $PID 
) [-c] [-<sigspec>] <pid>..." echo "Recursively kill the process tree(s) rooted by <pid>." echo "Options:" echo " -c Only kill children; don't kill root" echo " <sigspec> Arbitrary argument to pass to kill, expected to be signal specification" exit 1 } kill_parent=1 sig_spec=-9 function do_kill # <pid>... { kill "$sig_spec" "$@" } function kill_children # pid { local target= local pid= local ppid= local i # Returns alternating ids: first is pid, second is parent for i in $(ps -f | tail +2 | cut -b 10-24); do if [ ! -n "$pid" ]; then # first in pair pid=$i else # second in pair ppid=$i (( ppid == target && pid != $$ )) && { kill_children $pid do_kill $pid } # reset pid for next pair pid= fi done } test -n "" || usage while [ -n "" ]; do case "" in -c) kill_parent=0 ;; -*) sig_spec="" ;; *) kill_children (( kill_parent )) && do_kill ;; esac shift done

The only real downside is the somewhat ugly message that bash prints out when it receives a fatal signal, namely "Terminated", "Killed" or "Interrupted" (depending on what you send). However, I can live with that in batch scripts.

唯一真正的缺点是当 bash 收到致命信号时打印出的有点丑陋的消息,即“终止”、“杀死”或“中断”(取决于您发送的内容)。但是,我可以在批处理脚本中接受它。

回答by Adam Rosenfield

This scriptlooks like it'll do the job:

这个脚本看起来可以完成这项工作:

##代码##