【问题标题】:How to terminate script's process tree in Cygwin bash from bash script如何从 bash 脚本终止 Cygwin bash 中脚本的进程树
【发布时间】:2010-10-06 03:36:26
【问题描述】:

我有一个 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)也将终止 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 或等效的,因为可能有多个实例正在运行,我只想杀死特定的实例。

【问题讨论】:

    标签: bash process scripting cygwin terminate


    【解决方案1】:

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

    变化

    kill %1
    

    /bin/kill -- -$$
    

    为我工作。

    【讨论】:

    • 谢谢!奇怪的是,Cygwin kill 联机帮助页中没有记录这一点。但是,它确实适用于 Cygwin 版本的 kill。
    【解决方案2】:

    Adam 的链接让我找到了一个可以解决问题的方向,尽管并非没有一些小警告。

    脚本在 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】:

      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 
      

      【讨论】:

      • 脚本不能在 Cygwin 中未经修改地工作,但它是一个起点。赞成,但在我自己的答案中有一个工作脚本。
      猜你喜欢
      • 2011-12-10
      • 1970-01-01
      • 1970-01-01
      • 2011-05-10
      • 1970-01-01
      • 1970-01-01
      • 2012-07-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多