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 done
devuelva 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 $subshell
no funcionará porque$subshell
no es hijo del proceso que estás ejecutandowait
. De todos modos, no esperarás a que el proceso lo haga,wait
por lo que no importa mucho.kill $subshell
va a matar el subshell, pero nosleep
si el subshell había logrado iniciarlo en el momentokill
de la ejecución. Sin embargo, podrías ejecutarsleep
el 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 60
con exec sleep 60
si desea kill
matar sleep
y no solo el subshell, que en este caso podría ni siquiera tener tiempo de ejecutarse sleep
cuando 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 sleep
en segundo plano si eso es lo que desea hacer (o (sleep 600 &)
si desea ocultar ese sleep
proceso 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 sudo
genera 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). stdbuf
sin embargo, se ejecutará wpa_supplicant
en el mismo proceso, por lo que al final tendrás tres procesos (además del resto del script) en wpa_supplicant
la ascendencia de:
- la subcapa
- sudo cuando era niño de 1
- 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 sudo
sucede 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 root
y el resto del script no, no podrás eliminarlo tan fácilmente.
Necesitarías que kill
se 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_supplicant
se ejecutará en el mismo $BASHPID
proceso que el subshell con el que lo estamos creando exec
.
Pasamos el pid a través de la tubería y lo ejecutamos kill
como 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_supplicant
matado 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 grep
que desaparece.
Algunas implementaciones de shell no esperarían sudo
a que grep
haya 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 bash
no espera a que sudo
ninguno de los dos grep
haya 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 -i
shell 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 Terminated
mensaje.
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 Terminated
es 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 Terminated
mensajes 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 wait
salida del comando hace esto.