¿Matar silenciosamente a subshell?

¿Matar silenciosamente a subshell?

quiero implementar algo como estoPreguntas y respuestaspero para una subcapa. Aquí hay un ejemplo mínimo de lo que estoy intentando:

(subshell=$BASHPID
  (kill $subshell & wait $subshell 2>/dev/null) &
sleep 600)

echo subshell done

¿Cómo puedo hacer que solo subshell donedevuelva en lugar de:

./test.sh: line 4:  5439 Terminated              ( subshell=$BASHPID; ( kill $subshell && wait $subshell 2> /dev/null ) & sleep 600 )
subshell done

Editar: Puede que me equivoque con la terminología aquí; por subcapa me refiero al proceso dentro del primer conjunto de corchetes.

Actualizar:

Quiero publicar el fragmento del programa real para contextualizarlo. Lo anterior es una simplificación:

# If subshell below if killed or returns error connected variable won't be set
(if [ -n "$2" ];then

      # code to setup wpa configurations here

      # If wifi key is wrong kill subshell
      subshell=$BASHPID
      (sudo stdbuf -o0 wpa_supplicant -Dwext -i$wifi -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1 \
        | grep -m 1 "pre-shared key may be incorrect" \
        && kill -s PIPE "$subshell") &

      # More code which does the setup necessary for wifi

) && connected=true

# later json will be returned based on if connected is set

Respuesta1

Nota:

  • wait $subshellno funcionará porque $subshellno es hijo del proceso que estás ejecutando wait. De todos modos, no esperarás a que el proceso lo haga, waitpor lo que no importa mucho.
  • kill $subshellva a matar el subshell, pero no sleepsi el subshell había logrado iniciarlo en el momento killde la ejecución. Sin embargo, podrías ejecutar sleepel mismo proceso conexec
  • puedes usar SIGPIPE en lugar de SIGTERM para evitar el mensaje
  • dejar una variable sin comillas en contextos de lista tiene un significado muy especial en bash.

Dicho todo esto, puedes hacer:

(
  subshell=$BASHPID
  kill -s PIPE "$subshell" &
  sleep 600
)
echo subshell done

(reemplace sleep 60con exec sleep 60si desea killmatar sleepy no solo el subshell, que en este caso podría ni siquiera tener tiempo de ejecutarse sleepcuando lo elimine).

En cualquier caso, no estoy seguro de qué quieres lograr con eso.

sleep 600 &

sería una forma más confiable de comenzar sleepen segundo plano si eso es lo que desea hacer (o (sleep 600 &)si desea ocultar ese sleepproceso del shell principal)

Ahora con tu real

sudo stdbuf -o0 wpa_supplicant -Dwext -i"$wifi" -c/etc/wpa_supplicant/wpa_supplicant.conf

comando, tenga en cuenta que sudogenera un proceso secundario para ejecutar el comando (aunque solo sea porque es posible que necesite registrar su estado o realizar algunas tareas de sesión PAM posteriormente). stdbufsin embargo, se ejecutará wpa_supplicanten el mismo proceso, por lo que al final tendrás tres procesos (además del resto del script) en wpa_supplicantla ascendencia de:

  1. la subcapa
  2. sudo cuando era niño de 1
  3. wpa_supplicant (que anteriormente ejecutaba stdbuf) cuando tenía 2 años

Si matas a 1, eso no mata automáticamente a 2. Sin embargo, si matas a 2, a menos que sea con una señal como SIGKILL que no puede ser interceptada, eso matará a 3, como sudosucede al reenviar las señales que recibe al comando que ejecuta. .

En cualquier caso, esa no es la subcapa que querrías matar aquí, son 3 o al menos 2.

Ahora, si se está ejecutando rooty el resto del script no, no podrás eliminarlo tan fácilmente.

Necesitarías que killse hiciera como root, por lo que necesitarías:

sudo WIFI="$wifi" bash -c '
  (echo "$BASHPID" &&
   exec stdbuf -o0 wpa_supplicant -Dwext -i"$WIFI" -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1
  ) | {
    read pid &&
      grep -m1 "pre-shared key may be incorrect" &&
      kill -s PIPE "$pid"
  }'

De esa manera, wpa_supplicantse ejecutará en el mismo $BASHPIDproceso que el subshell con el que lo estamos creando exec.

Pasamos el pid a través de la tubería y lo ejecutamos killcomo root.

Tenga en cuenta que si está dispuesto a esperar un poco más,

sudo stdbuf -o0 wpa_supplicant -Dwext -i"$wifi" -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1 |
  grep -m1 "pre-shared key may be incorrect"

Habría wpa_supplicantmatado automáticamente con un SIGPIPE (por el sistema, por lo que no hay problema de permiso) elLa próxima vezescribe algo en esa tubería después de grepque desaparece.

Algunas implementaciones de shell no esperarían sudoa que grephaya regresado (dejándolo ejecutándose en segundo plano hasta que se SIGPIPEP), y con bash, también puedes hacerlo usando la grep ... <(sudo ...)sintaxis, donde bashno espera a que sudoninguno de los dos grephaya regresado.

Mas en¿Grep tarda en salir después de encontrar una coincidencia?

Respuesta2

Subshell se refiere a un comando de shell que es hijo de algún shell, como el hijo del bash -ishell de inicio de sesión interactivo que le ofrece un $mensaje. tu notenerpara ejecutar su comando en un subshell; puede optar por ejecutarlo como un proceso independiente. Parece que esto puede ser apropiado porque no quiere que su stdout/stderr arruine la apariencia de su barra de progreso, y porque no quiere que el shell principal informe o incluso note la muerte de su hijo.

Existen herramientas estándar para lograrlo, comodemonizary nohup. (Ver tambiénhombre paginas.) Es posible que le resulte mejornohup. A continuación se muestra un ejemplo de cómo usarlo para ejecutar un programa trivial, que nonocrear nohup.out:

$ nohup true 2>&1 > /dev/null &

Haga que su programa, o un script contenedor para su programa, registre su PID en /tmp/my.pid; bash lo pone a disposición como $$variable. Entonces el proceso de seguimiento con la barra de progreso puede

$ kill `cat /tmp/my.pid`

cuando ya no necesita que ese programa realice más procesamiento. Alternativamente, es posible que prefieras darle el nombre de tu programa a killall.

Respuesta3

Quizás estés buscando esto

#!/bin/bash
(subshell=$BASHPID
  (kill $subshell & wait $subshell 2>/dev/null) &
sleep 600) &
wait $! 2>/dev/null

echo subshell done

aquí el subshell se pone en segundo plano, luego el shell principal espera pero con la salida de espera enviada a /dev/null. Esto capta el Terminatedmensaje.

Tenga en cuenta que si cambia esperar para capturar la salida al archivo, por ejemplo wait $! 2>wait_output, verá

 ./foo.sh: line 5:  1939 Terminated              ( subshell=$BASHPID; ( kill $subshell & wait $subshell 2> /dev/null ) & sleep 600 )

mostrando que Terminatedes del shell principal.

Una pequeña comprobación para ver que funciona si hay alguna actividad antes de matar es

#!/bin/bash
(subshell=$BASHPID
 (sleep 1; kill $subshell & wait $subshell 2>/dev/null) &
sleep 600) & wait 2>wait_output

echo subshell done

Este ejemplo se detendrá por un segundo antes de imprimir subshell done. Este ejemplo también muestra cómo poner en segundo plano y esperar todo en la misma línea, por ejemplo & wait 2>wait_output. No estoy seguro de si eso tiene alguna ventaja/desventaja sobre el ejemplo con wait $!.

Lo clave a tener en cuenta aquí es que los Terminatedmensajes provienen del control de trabajo del shell principal de nivel superior. Eso es lo que ve terminar la subcapa y genera el mensaje. Entonces ahí es donde desea capturar el resultado. Redirigir la waitsalida del comando hace esto.

información relacionada