代码之家  ›  专栏  ›  技术社区  ›  Barry Kelly

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

  •  4
  • Barry Kelly  · 技术社区  · 17 年前

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

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

    #!/bin/bash
    
    test -f kill_me && rm kill_me
    
    touch kill_me
    tail -f kill_me
    

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

    然而,当我试图从剧本中做同样的事情时,我发现只有 bash 运行脚本的进程终止,同时 挂在与父母断开连接的地方。这是我尝试过的一种方法( 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将终止,但 仍然在附近徘徊。

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

    运行此脚本后仍在运行。

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

    3 回复  |  直到 17 年前
        1
  •  6
  •   andrewdotn    17 年前

    /bin/cill(程序,而不是bash内置)解释一个 负面的 PID会杀死进程组,进程组也会得到所有的子进程。

    变化

    kill %1
    

    /bin/kill -- -$$
    

    为我工作。

        2
  •  2
  •   Barry Kelly    17 年前

    亚当的联系让我找到了解决问题的方向,尽管并非没有一些小的警告。

    该脚本在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在收到致命信号时打印出有点难看的消息,即“终止”、“杀死”或“中断”(取决于你发送的内容)。然而,我可以在批处理脚本中接受这一点。

        3
  •  1
  •   Adam Rosenfield    17 年前

    This script 看起来它能胜任工作:

    #!/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 
    
    推荐文章