salida confirmada con trampa

9

Estoy tratando de atrapar la Ctrl+Cseñal pidiendo una confirmación del usuario. La parte de captura funciona bien. Pero una vez que la señal queda atrapada, no vuelve a la ejecución normal. En cambio, sale del guión. Cómo hacer que reanude la ejecución cuando el usuario presiona no.

aqui esta mi codigo

hell()
{
echo "Do you want to quit? Press 1 for yes and 0 for no";
read n;
if [ $n == 1 ]; then
exit 1;
fi
}

trap "hell" SIGINT

find /
CHID
fuente

Respuestas:

12

Qué esta pasando

Cuando presiona Ctrl+ C, la SIGINTseñal se entrega a todo el grupo de procesos en primer plano . Aquí se envía tanto al findproceso como al proceso de shell de llamada. findreacciona saliendo inmediatamente, y el caparazón reacciona llamando a la trampa.

Si el código en la trampa regresa (es decir, no llama exit), la ejecución continúa con el comando después del que fue interrumpido por la señal. Aquí, después de que el findcomando llega al final del script, el script se cierra inmediatamente de todos modos. Pero puede ver la diferencia entre ingresar 0 y 1 agregando otro comando:

find /
echo "find returned $?"

Una forma de hacer lo que quieres (pero probablemente no deberías hacer)

Puedes hacer lo que quieras; pero esta parte de mi respuesta es más sobre descubrir la programación de shell que resolver un problema real.

  • Como cuestión de diseño, una señal reiniciable no es lo que normalmente esperaría en el tipo de programas relativamente simples que normalmente son los scripts de shell. La expectativa es que Ctrl+ Cmatará el script.
  • Como verá a continuación, de todos modos, extiende las capacidades del shell un poco lejos.

Si desea evitar la muerte find, lo que necesita para iniciarlo en el fondo : find / &. Luego use el waitincorporado para esperar a que salga normalmente. Una señal interrumpirá el waitincorporado, que puede ejecutar en un bucle hasta que reciba una señal que desea propagar. Luego úsalo killpara matar el trabajo.

hell () {
  echo "Do you want to quit? Press 1 for yes and 0 for no"
  read n
  if [ "$n" = 1 ]; then
    # Kill the job if it's running, then exit
    if [ -n "$job_pid" ]; then kill $job_pid; fi
    exit 1
  fi
}

job_pid=
trap "hell" SIGINT

# Start a demo job in the background
for i in 1 2 3 4 5; do date; sleep 1; done &
job_pid=$!
# Call wait in a loop; wait will return 0 if the job exits, and 128+$signum if interrupted by a signal.
while ! wait; do
  echo "resuming wait"
done
job_pid=
echo last exit code: $?

Existen limitaciones para este enfoque en el shell:

  • Hay una condición de carrera: si presiona Ctrl+ Cjusto después de que el trabajo haya terminado pero antes de la job_pid=línea, el controlador de señal intentará matar $jobpid, pero el proceso ya no existe (incluso como un zombi, porque waitya lo ha cosechado), y el proceso La identificación puede haber sido reutilizada por otro proceso. Esto no se puede arreglar fácilmente en el shell (¿tal vez configurando un controlador para SIGCHLD?).
  • Si necesita el estado de devolución del trabajo, debe usar el wait $job_pidformulario. Pero entonces no se puede distinguir " waitfue interrumpido por una señal" de "el trabajo fue cancelado por una señal" (ni de "el trabajo finalizó por sí solo con un estado de retorno ≥128", pero eso es un hecho general programación).
  • Esto no se extenderá fácilmente si es que lo hace a múltiples subjobs. Tenga en cuenta que el comportamiento de las trampas y las señales a menudo es sorprendente cuando va más allá de lo básico en la mayoría de las implementaciones de shell (solo ksh lo hace bien).

Para superar estas limitaciones, use un lenguaje más sofisticado como Perl o Python.

Gilles 'SO- deja de ser malvado'
fuente