4

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

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

#!/bin/bash

test -f kill_me && rm kill_me

touch kill_me
tail -f kill_me

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

但是,当我尝试从脚本做同样的事情时,我发现只有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

如果运行它,后台运行的 bash 子shell 会正常终止,但tail仍会挂起。

如果我像这样使用显式单独的脚本,它仍然不起作用(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

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

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

4

3 回答 3

6

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

改变

kill %1

/bin/kill -- -$$

为我工作。

于 2009-02-09T02:38:23.220 回答
2

亚当的链接使我朝着可以解决问题的方向前进,尽管并非没有一些小警告。

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

#!/bin/bash

function usage
{
    echo "usage: $(basename $0) [-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=$1
    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 "$1" || usage

while [ -n "$1" ]; do
    case "$1" in
        -c)
            kill_parent=0
            ;;

        -*)
            sig_spec="$1"
            ;;

        *)
            kill_children $1
            (( kill_parent )) && do_kill $1
            ;;
    esac
    shift
done

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

于 2009-02-07T19:04:55.413 回答
1

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

#!/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=$1
    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,\}\) .*/\1 \2/g'`
    do
        if [ "$aprocess" = "" ]; then
            aprocess=$x
            itsparent=""
            continue
        else
            itsparent=$x
            if [ "$itsparent" = "$pid" ]; then
                dokill $aprocess
            fi
            aprocess=""
        fi
    done
    echo "killing $1"
    kill -9 $1 > /dev/null 2>&1
}

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

dokill $PID 
于 2009-02-07T15:18:48.380 回答