Si uso trap
como se describe, por ejemplo, en http://linuxcommand.org/wss0160.php#trap para capturar ctrl-c (o similar) y limpiar antes de salir, entonces estoy cambiando el código de salida devuelto.
Ahora, esto probablemente no hará la diferencia en el mundo real (por ejemplo, porque los códigos de salida no son portátiles y, además, no siempre son inequívocos, como se discute en Código de salida predeterminado cuando finaliza el proceso ), pero todavía me pregunto si hay ¿Realmente no hay forma de evitar eso y devolver el código de error predeterminado para los scripts interrumpidos?
Ejemplo (en bash, pero mi pregunta no debe considerarse específica de bash):
#!/bin/bash
trap 'echo EXIT;' EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. ' _
trap 'echo SIGINT; exit 1;' INT
read -p 'If you ctrl-c me now my return code will be 1. ' _
Salida:
$ ./test.sh # doing ctrl-c for 1st read
If you ctrl-c me now my return code will be the default for SIGTERM.
$ echo $?
130
$ ./test.sh # doing ctrl-c for 2nd read
If you ctrl-c me now my return code will be the default for SIGTERM.
If you ctrl-c me now my return code will be 1. SIGINT
EXIT
$ echo $?
1
(Editado para eliminar para que sea más compatible con POSIX).
(Editado nuevamente para convertirlo en un script bash, mi pregunta no es específica de shell).
Editado para usar el "INT" portátil para la trampa en favor del "SIGINT" no portátil.
Editado para eliminar llaves inútiles y agregar una solución potencial.
Actualizar:
Lo resolví ahora simplemente saliendo con algunos códigos de error codificados y atrapando EXIT. Esto puede ser problemático en ciertos sistemas porque el código de error puede diferir o la trampa EXIT no es posible, pero en mi caso está bien.
trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM
read
que lea desde el coproceso actual ytrap cmd SIGINT
no funcionará como el estándar dice que debe usartrap cmd INT
.read -p
lee las entradas del coproceso actual.Respuestas:
Todo lo que necesita hacer es cambiar el controlador EXIT dentro de su controlador de limpieza. Aquí hay un ejemplo:
fuente
trap cleanup INT
lugar detrap cleanup EXIT
?En realidad, la interrupción interna de bash
read
parece ser un poco diferente a la interrupción de un comando ejecutado por bash. Normalmente, cuando ingresastrap
,$?
está configurado y puedes conservarlo y salir con el mismo valor:Si su secuencia de comandos se interrumpe al ejecutar un comando como
sleep
o incluso como incorporadowait
, veráy el código de salida es 130. Sin embargo,
read -p
parece que$?
es 0 (en mi versión de bash 4.3.42 de todos modos).El manejo de señales durante el
read
trabajo podría estar en progreso, de acuerdo con el archivo de cambios en mi versión ... (/ usr / share / doc / bash / CHANGES)fuente
read
, como se indica en el archivo CAMBIOS (agregado a mi respuesta). Entonces, podría ser un trabajo en progreso.128 + signo
como código de salida para las señales, ksh93 usa256 + signo
. POSIX dice: algo por encima de 128 ...Cualquier código de salida de señal habitual estará disponible
$?
al ingresar al controlador de trampa:Si hay una trampa de SALIDA separada, puede usar la misma metodología: mantenga inmediatamente el estado de salida pasado por la limpieza del controlador de señal (si hay una), luego devuelva el estado de salida guardado.
fuente
local
aunque no es POSIX.{ba,z}sh
. AFAIK, es lo mejor que se puede hacer, independientemente.$?
es 1 cualquiera que sea la señal recibida).Simplemente devolver algún código de error no es suficiente para simular una salida de SIGINT. Me sorprende que nadie haya mencionado esto hasta ahora. Lectura adicional: https://www.cons.org/cracauer/sigint.html
La forma correcta es:
Esto funciona con Bash, Dash y zsh. Para una mayor portabilidad, debe usar especificaciones de señal numérica (por otro lado, zsh espera un parámetro de cadena para el
kill
comando ...)También tenga en cuenta el tratamiento especial de la
EXIT
señal. Esto se debe a que algunos shells (es decir, Bash) también ejecutan trampasEXIT
para cualquier señal (después de posibles trampas definidas en esa señal). El reinicio de laEXIT
trampa lo impide.Ejecutar código solo en la salida "normal"
La verificación
[ $sig = EXIT ]
permite ejecutar código solo en la salida normal (no señalizada). Sin embargo, todas las señales tienen que tener trampas que finalmente reinicien la trampaEXIT
;normal_exit_only_cleanup
También se llamará para las señales que no lo hacen. También se ejecutará por una salida a travésset -e
. Esto se puede solucionar atrapándoloERR
(que no es compatible con Dash) y agregando una marca de verificación[ $sig = ERR ]
antes dekill
.Versión simplificada de solo Bash
Por otro lado, este comportamiento significa que en Bash simplemente puedes hacer
simplemente ejecutar un código de limpieza y retener el estado de salida.
editado
El comportamiento elaborado de Bash de "EXIT atrapa todo"
Eliminar la señal KILL, que no puede quedar atrapada
Eliminar los prefijos SIG de los nombres de señal
No intentes
kill -s EXIT
Tomar
set -e
/ ERR en cuentafuente
SIGINT
es la forma de cerrarlo y puede decidir salir con 0 si logró terminar sin error u otro código de error si no se cerró limpiamente. El caso en cuestión:top
. Ejecutatop; echo $?
y luego presiona Ctrl-C. El estado volcado en la pantalla será 0.