El lenguaje exacto utilizado en la especificación Single UNIX para describir el significado deset -e
es:
Cuando esta opción está activada, si un comando simple falla por alguno de los motivos enumerados en Consecuencias de los errores del shell o devuelve un valor de estado de salida> 0, y no es [un comando condicional o negado], el shell se cerrará inmediatamente.
Existe una ambigüedad en cuanto a lo que sucede cuando dicho comando se produce en una subshell . Desde un punto de vista práctico, todo lo que el subshell puede hacer es salir y devolver un estado distinto de cero al shell principal. El hecho de que el shell primario salga a su vez depende de si este estado distinto de cero se traduce en un simple comando que falla en el shell primario.
Uno de estos casos problemáticos es el que encontró: un estado de retorno distinto de cero de una sustitución de comando . Como este estado se ignora, no hace que el shell principal salga. Como ya descubrió , una forma de tener en cuenta el estado de salida es utilizar la sustitución de comando en una asignación simple : entonces el estado de salida de la asignación es el estado de salida de la última sustitución de comando en la (s) asignación (es) .
Tenga en cuenta que esto funcionará según lo previsto solo si hay una única sustitución de comando, ya que solo se tiene en cuenta el estado de la última sustitución. Por ejemplo, el siguiente comando es exitoso (de acuerdo con el estándar y en cada implementación que he visto):
a=$(false)$(echo foo)
Otro caso a tener en cuenta es subniveles explícitas : (somecommand)
. Según la interpretación anterior, el subshell puede devolver un estado distinto de cero, pero dado que este no es un comando simple en el shell principal, el shell principal debe continuar. De hecho, todos los proyectiles que conozco hacen que los padres regresen en este punto. Si bien esto es útil en muchos casos, como (cd /some/dir && somecommand)
cuando los paréntesis se usan para mantener una operación como un cambio de directorio actual local, viola la especificación si set -e
se desactiva en la subshell, o si la subshell devuelve un estado distinto de cero de una manera que no lo terminaría, como usar !
un comando verdadero. Por ejemplo, todas las cenizas, bash, pdksh, ksh93 y zsh salen sin mostrarse foo
en los siguientes ejemplos:
set -e; (set +e; false); echo "This should be displayed"
set -e; (! true); echo "This should be displayed"
¡Sin embargo, ningún comando simple ha fallado mientras set -e
estaba en vigor!
Un tercer caso problemático son los elementos en una tubería no trivial . En la práctica, todos los shells ignoran las fallas de los elementos de la tubería que no sean la última, y exhiben uno de los dos comportamientos con respecto al último elemento de la tubería:
- ATT ksh y zsh, que ejecutan el último elemento de la canalización en el shell principal, funcionan como de costumbre: si un comando simple falla en el último elemento de la canalización, el shell ejecuta ese comando, que resulta ser el shell principal, salidas
- Otros shells aproximan el comportamiento al salir si el último elemento de la tubería devuelve un estado distinto de cero.
Al igual que antes, apagar set -e
o usar una negación en el último elemento de la tubería hace que devuelva un estado distinto de cero de una manera que no debería terminar el shell; los shells que no sean ATT ksh y zsh saldrán.
La pipefail
opción de Bash hace que una tubería salga inmediatamente debajo set -e
si alguno de sus elementos devuelve un estado distinto de cero.
Tenga en cuenta que, como complicación adicional, bash se apaga set -e
en subcapas a menos que esté en modo POSIX ( set -o posix
o tenga POSIXLY_CORRECT
en el entorno cuando se inicia bash).
Todo esto muestra que la especificación POSIX desafortunadamente hace un mal trabajo al especificar la -e
opción. Afortunadamente, las conchas existentes son en su mayoría consistentes en su comportamiento.