Pregunta

Tengo un script bash Cygwin que necesito para ver y terminar bajo ciertas condiciones - en concreto, después de un determinado archivo se ha creado. Estoy teniendo dificultades para averiguar cómo exactamente para terminar el guión con el mismo nivel de integridad que Ctrl + C hace, sin embargo.

Aquí hay un script sencillo (llamado test1 ) que no hace más que esperar para ser terminado.

#!/bin/bash

test -f kill_me && rm kill_me

touch kill_me
tail -f kill_me

Si este script se ejecuta en el primer plano, Ctrl + C terminará tanto el tail y el propio guión. Si el script se ejecuta en segundo plano, una kill %1 (suponiendo que es el trabajo 1) también se resolverán tanto tail y el guión.

Sin embargo, cuando trato de hacer lo mismo a partir de un guión, me estoy encontrando que sólo el proceso bash ejecuta el script es terminado, mientras que cuelga alrededor tail desconectado de su matriz. He aquí una manera Probé ( 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

Si esto se ejecuta, la fiesta subnivel ejecuta en segundo plano se termina bien, pero tail todavía cuelga alrededor.

Si utilizo un script de forma explícita por separado, como este, todavía no funciona ( 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 todavía está dando vueltas después de este script se ejecuta.

En mi caso concreto, el proceso de creación de archivos no es particularmente instrumentable, así que no puedo conseguirlo para terminar por sí misma; averiguando cuando se ha creado un archivo en particular, sin embargo, puedo en ese momento saber que está bien que por terminado. Por desgracia, no puedo usar un killall simple o equivalente, ya que puede haber varias instancias en ejecución, y yo sólo quiero matar a la instancia específica.

¿Fue útil?

Solución

/ bin / kill (el programa, no la orden interna bash) interpreta un negativa PID como “matar al grupo de procesos” que le llevará a todos los niños también.

Cambio

kill %1

a

/bin/kill -- -$$

funciona para mí.

Otros consejos

Enlace de Adam me puso en una dirección que va a resolver el problema, aunque no sin algunas advertencias menores.

El script no funciona sin modificar bajo Cygwin, por lo que volvió a escribir, y con un par de opciones. Aquí está mi versión:

#!/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

El único inconveniente real es el mensaje algo feo que bash imprime cuando se recibe una señal fatal, a saber, "Terminado", "muertos" o "interrumpida" (dependiendo de lo que se envía). Sin embargo, puedo vivir con eso en secuencias de comandos por lotes.

Este script parece que 'll hacer el trabajo:

#!/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 
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top