set -e
finaliza el script si se encuentra un código de salida distinto de cero, excepto bajo ciertas condiciones. Para resumir los peligros de su uso en pocas palabras: no se comporta como la gente piensa que lo hace.
En mi opinión, debe considerarse como un hack peligroso que continúa existiendo solo con fines de compatibilidad. La set -e
declaración no convierte el shell de un lenguaje que usa códigos de error en un lenguaje que usa un flujo de control similar a una excepción, simplemente intenta mal emular ese comportamiento.
Greg Wooledge tiene mucho que decir sobre los peligros de set -e
:
En el segundo enlace, hay varios ejemplos del comportamiento poco intuitivo e impredecible de set -e
.
Algunos ejemplos del comportamiento poco intuitivo de set -e
(algunos tomados del enlace wiki anterior):
conjunto -e
x = 0
dejar x ++
echo "x es $ x"
Lo anterior hará que el script de shell se cierre prematuramente, porque let x++
devuelve 0, que la let
palabra clave trata como un valor falso y se convierte en un código de salida distinto de cero. set -e
se da cuenta de esto y termina silenciosamente el script.
conjunto -e
[-d / opt / foo] && echo "Advertencia: foo ya está instalado. Se sobrescribirá". > Y 2
echo "Instalando foo ..."
Lo anterior funciona como se esperaba, imprimiendo una advertencia si /opt/foo
ya existe.
conjunto -e
check_previous_install () {
[-d / opt / foo] && echo "Advertencia: foo ya está instalado. Se sobrescribirá". > Y 2
}
check_previous_install
echo "Instalando foo ..."
Lo anterior, a pesar de que la única diferencia es que una sola línea se ha refactorizado en una función, finalizará si /opt/foo
no existe. Esto se debe a que el hecho de que funcionó originalmente es una excepción especial al set -e
comportamiento de. Cuando a && b
devuelve un valor distinto de cero, es ignorado por set -e
. Sin embargo, ahora que es una función, el código de salida de la función es igual al código de salida de ese comando, y la función que devuelve un valor distinto de cero terminará silenciosamente el script.
conjunto -e
IFS = $ '\ n' read -d '' -r -a config_vars <config
Lo anterior leerá la matriz config_vars
del archivo config
. Como el autor podría pretender, termina con un error si config
falta. Como el autor podría no tener la intención, termina silenciosamente si config
no termina en una nueva línea. Si set -e
no se usara aquí, config_vars
contendría todas las líneas del archivo, ya sea que terminara o no en una nueva línea.
Los usuarios de Sublime Text (y otros editores de texto que manejan líneas nuevas incorrectamente), tengan cuidado.
conjunto -e
should_audit_user () {
grupos de grupos locales = "$ (grupos" $ 1 ")"
para grupo en $ grupos; hacer
si ["$ grupo" = auditoría]; luego devuelve 0; fi
hecho
volver 1
}
if should_audit_user "$ usuario"; entonces
registrador 'Blah'
fi
El autor aquí podría esperar razonablemente que si por alguna razón el usuario $user
no existe, el groups
comando fallará y el script terminará en lugar de permitir que el usuario realice alguna tarea sin auditar. Sin embargo, en este caso la set -e
terminación nunca surte efecto. Si $user
no se puede encontrar por alguna razón, en lugar de terminar el script, la should_audit_user
función simplemente devolverá datos incorrectos como si set -e
no estuvieran vigentes.
Esto se aplica a cualquier función invocada desde la parte de condición de una if
declaración, sin importar cuán profundamente anidada, sin importar dónde esté definida, sin importar si se ejecuta set -e
dentro de ella nuevamente. El uso if
en cualquier punto deshabilita completamente el efecto set -e
hasta que el bloque de condición se ejecute completamente. Si el autor no es consciente de esta trampa, o no conoce toda su pila de llamadas en todas las situaciones posibles en las que se puede invocar una función, entonces escribirán un código defectuoso y la falsa sensación de seguridad proporcionada set -e
será al menos parcialmente culpa.
Incluso si el autor es plenamente consciente de este escollo, la solución consiste en escribir el código de la misma manera en que se escribiría sin él set -e
, haciendo que ese interruptor sea menos que inútil; el autor no solo tiene que escribir el código de manejo manual de errores como si set -e
no estuviera vigente, sino que la presencia de los set -e
puede haber engañado haciéndoles creer que no tienen que hacerlo.
Algunos inconvenientes adicionales de set -e
:
- Alienta el código descuidado. Los manejadores de errores se olvidan por completo, con la esperanza de que cualquier cosa que falle informe el error de alguna manera sensata. Sin embargo, con ejemplos como los
let x++
anteriores, este no es el caso. Si el script muere inesperadamente, generalmente es silencioso, lo que dificulta la depuración. Si el script no muere y esperaba que lo hiciera (vea el punto anterior), entonces tiene un error más sutil e insidioso en sus manos.
- Lleva a las personas a una falsa sensación de seguridad. Ver de nuevo la
if
viñeta de condición.
- Los lugares donde termina el shell no son consistentes entre shells o versiones de shell. Es posible escribir accidentalmente un script que se comporta de manera diferente en una versión anterior de bash debido al comportamiento de
set -e
haber sido modificado entre esas versiones.
set -e
es un tema polémico, y algunas personas conscientes de los problemas que lo rodean recomiendan no hacerlo, mientras que otros simplemente recomiendan tener cuidado mientras están activos para conocer las trampas. Hay muchos novatos de scripting de shell que recomiendan set -e
en todos los scripts como un todo para condiciones de error, pero en la vida real no funciona de esa manera.
set -e
No es un sustituto de la educación.