Me he encontrado con algunas secuencias de comandos como esta recientemente:
( set -e ; do-stuff; do-more-stuff; ) || echo failed
Esto me parece bien, ¡pero no funciona! El set -e
no se aplica, cuando se agrega el ||
. Sin eso, funciona bien:
$ ( set -e; false; echo passed; ); echo $?
1
Sin embargo, si agrego el ||
, set -e
se ignora el:
$ ( set -e; false; echo passed; ) || echo failed
passed
El uso de un shell real separado funciona como se esperaba:
$ sh -c 'set -e; false; echo passed;' || echo failed
failed
He intentado esto en varios shells diferentes (bash, dash, ksh93) y todos se comportan de la misma manera, por lo que no es un error. ¿Alguien puede explicar esto?
shell
shell-script
Científico loco
fuente
fuente
||
exterior de la subshell afecta el comportamiento dentro de la subshell.(set -e; echo 1; false; echo 2)
con(set -e; echo 1; false; echo 2) || echo 3
Respuestas:
De acuerdo con este hilo , es el comportamiento que POSIX especifica para usar "
set -e
" en una subshell.(También me sorprendió).
Primero, el comportamiento:
Las notas del segundo post,
Hay un poco más en el cuarto post, también de Eric Blake,
Este comportamiento es definitivamente sorprendente. Es contrario a la intuición: uno esperaría que la reactivación
set -e
tuviera un efecto, y que el contexto circundante no tendría precedentes; Además, la redacción del estándar POSIX no deja esto particularmente claro. Si lo lee en el contexto donde el comando falla, la regla no se aplica: solo se aplica en el contexto circundante, sin embargo, se aplica por completo.fuente
set -e; (false; echo passed;) || echo failed
. No me sorprende, en realidad, que -e se ignore en este caso dada la redacción de la norma. Sin embargo, en mi caso, configuro explícitamente -e en la subshell y espero que la subshell salga en caso de falla. No hay una lista AND-OR en el subshell ...if
y||
y&&
son infecciosas? esto es absurdoDe hecho,
set -e
no tiene ningún efecto dentro de las subcapas si usa el||
operador después de ellas; por ejemplo, esto no funcionaría:Aaron D. Marasco en su respuesta hace un gran trabajo al explicar por qué se comporta de esta manera.
Aquí hay un pequeño truco que puede usarse para solucionar esto: ejecute el comando interno en segundo plano y luego espere inmediatamente. El
wait
incorporado devolverá el código de salida del comando interno, y ahora está usando||
despuéswait
, no la función interna, por lo queset -e
funciona correctamente dentro de este último:Aquí está la función genérica que se basa en esta idea. Debería funcionar en todos los shells compatibles con POSIX si elimina las
local
palabras clave, es decir, reemplace todolocal x=y
con solox=y
:Ejemplo de uso:
Ejecutando el ejemplo:
Lo único que debe tener en cuenta al usar este método es que todas las modificaciones de las variables de Shell realizadas desde el comando al que pasa
run
no se propagarán a la función de llamada, porque el comando se ejecuta en una subshell.fuente
No descartaría que sea un error solo porque varios proyectiles se comportan de esa manera. ;-)
Tengo más diversión para ofrecer:
¿Puedo citar a man bash (4.2.24):
Quizás la evaluación sobre varios comandos lleva a ignorar el || contexto.
fuente
eval
truco no me funciona. Intenté bash, bash en modo posix y dash.set -e
.set -e
Está roto por diseño. No lo usaría para nada más que los scripts de shell más simples sin estructuras de control, subcapas o sustituciones de comandos.Solución alternativa cuando usint toplevel
set -e
Llegué a esta pregunta porque estaba usando
set -e
como método de detección de errores:y sin
||
, el script dejaría de ejecutarse y nunca llegaríado_more_stuff
.Como parece que no hay una solución limpia, creo que solo haré un simple
set +e
en mis scripts:fuente