
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
a=$(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
echo -n $(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
survived
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
f() { :; }
f $(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
survived
¿Algún otro caso? ¿O alguna generalización?
Respuesta1
TL, DR: para beneficiarse set -e
, asigne directamente el resultado de una sustitución de comando a una variable (opcionalmente con cadenas adicionales alrededor). No combine varias sustituciones de comandos ni utilice una sustitución de comandos en un argumento de comando.
El problema no es con inherit_errexit
. Esta funcionando. El problema son las limitaciones de set -e
(que no son específicas de bash: otros shells tipo sh tienen el mismo problema).
Demostración: ejecute esta variante de su segundo ejemplo.
$ cat b2.sh
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
echo -n $(cat no-such-file; echo >&2 after cat)
echo survived
$ ./b2.sh
cat: no-such-file: No such file or directory
survived
Tenga en cuenta que echo >&2 after cat
no se ejecutó. Lo sería si inherit_errexit
estuviera apagado.
El problema es que set -e
sólo detiene la ejecución por error en casos simples. Si la sustitución de un comando devuelve un estado de error, eso no detiene la ejecución del comando simple que contiene la sustitución. Como máximo, podría establecer el estado de retorno del comando simple, lo que a su vez podría detener la ejecución del script. Un "comando simple" consta de asignaciones, redirecciones y un nombre y argumentos de comando ejecutable opcionales. Si falla una redirección, el estado de retorno es 1. De lo contrario, si hay un nombre de comando, el estado de retorno del comando simple es el estado de retorno del comando ejecutable. De lo contrario, el estado de devolución es el estado de devolución de la última sustitución de comando, o 0 si no hay ninguna. Aquí hay algunos ejemplos de comandos simples:
true </no/such/file
→ estado 1 debido a la redirección fallidafalse </dev/null
→ estado 1 defalse
a=b
→ estado 0 por no tener ninguna pieza que pueda fallara=$(exit 0) b=$(exit 1) c=$(exit 2)
→ estado 2 desde la última sustitución de comandoa=$(exit 2) b=$(exit 1) c=$(exit 0)
→ estado 0 desde la última sustitución de comandotrue $(exit 0) $(exit 1) $(exit 2)
→ estado 0 detrue
Una vez más, en todos los casos, set -e
sólo detendrá el script si el estado del comando es distinto de cero. El estado de los comandos integrados no es directamente relevante.
Y así, en su segundo script echo -n $(…)
tiene el estado 0, desde echo
(excepto si echo
no se puede escribir), independientemente de lo que suceda dentro de la sustitución del comando. Por lo tanto, el script no se detendrá aquí incluso si set -e
está activo. Asimismo, en el tercer script, f $(…)
tiene el estado 0 de f
.