Considerando un software de línea de comando personalizado (por ejemplo, loopHelloWorld) que detecta Ctrl+C para un apagado agradable. ¿Cómo canalizar la respuesta sin perder el Ctrl+C
?
$ loopHelloWorld
- Ctrl+C to nicely shutdown
Pero con la tubería, la tubería mata el software sin un cierre agradable.
$ loopHelloWorld |
while IFS= read -r line; do
echo "$line"
done
Ejemplo
ping example.com |
while IFS= read -r line; do
echo "$line"
done
Respuesta1
Ctrl+Chace que se envíe un SIGINT a todos los procesos en la canalización (ya que todos se ejecutan en el mismo grupo de procesos que corresponde a ese trabajo de primer plano de su shell interactivo).
Entonces en:
loopHelloWorld |
while IFS= read -r line; do
echo "$line"
done
Tanto el proceso en ejecución loopHelloWorld
como el que ejecuta el subshell que ejecuta el while
bucle obtendrán el archivo SIGINT
.
Si loopHelloWorld
escribe el Ctrl+C to nicely shutdown
mensaje en su salida estándar, también se escribirá en la tubería. si eso esdespuésla subcapa en el otro extremo ya ha muerto, loopHelloWorld
entoncestambiénrecibir un SIGPIPE, que necesitarías manejar.
Aquí, debes escribir ese mensaje en stderr ya que no es la salida normal de tu comando ( ping
aunque no se aplica al ejemplo). Entonces no pasaría por la tubería.
O podría hacer que el subshell que ejecuta el bucle while ignore el SIGINT para que siga leyendo la loopHelloWorld
salida después del SIGINT:
loopHelloWorld | (
trap '' INT
while IFS= read -r line; do
printf '%s\n' "$line"
done
)
Sin embargo, eso causaría que el estado de salida de la tubería sea 0 cuando presione Ctrl+C.
Otra opción para ese ejemplo específico sería usar zsh
o ksh93
en lugar de bash
. En esos shells, el while
bucle se ejecutaría en el proceso principal del shell, por lo que SIGINT no lo afectaría.
Eso no ayudaría a loopHelloWorld | cat
determinar dónde cat
y loopHelloWorld
ejecutarlo en el grupo de procesos de primer plano.
Respuesta2
Usa una trampa:
#! /bin/bash
trap handleInt SIGINT
interrupted=0
function handleInt {
echo "Interrupted..."
interrupted=1
}
while true
do
[[ interrupted -ne 0 ]] && { echo "Ctrl-C caught, exiting..." ; exit ; }
echo "Sleeping..."
sleep 1
read -r line
echo "$line"
done