2009-02-07 98 views
3

我有一个Cygwin bash脚本,我需要在特定条件下观察并终止 - 具体来说,在创建某个文件后。不过,我很难理解如何以与Ctrl + C完全相同的级别终止脚本。如何从bash脚本中终止脚本的Cygwin bash中的进程树

这是一个简单的脚本(称为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终止OK,但仍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或等价物,因为可能有多个实例在运行,而我只想杀死特定的实例。

回答

6

/bin/kill(程序,而不是bash内建函数)将作为负值 PID解释为“杀死进程组”,这也会让所有的孩子都得到。

更改

kill %1 

/bin/kill -- -$$ 

为我工作。

+0

谢谢!奇怪的是,这并没有在Cygwin kill man页面中记录。但是,它确实与Cygwin版本的kill相配合。 – 2009-02-09 10:15:06

1

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 
+0

该脚本在Cygwin中未修复,但它是一个起点。 Upvoted,但在我自己的答案中有一个工作脚本。 – 2009-02-07 19:05:28

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" "[email protected]" 
} 

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打印出来,当它接收到一个致命的信号,即“终止”,“封杀”或“追访”(这取决于你送什么) 。不过,我可以在批处理脚本中使用它。