El siguiente script tiene un error de sintaxis o algún tipo de error:
#!/usr/bin/env bash
set -euo pipefail
if [ ! -f /custom.log]; then
echo "test"
fi
abcxyz
El script falla con la salida de:
./test.sh: line 4: [: missing `]'
./test.sh: line 7: abcxyz: command not found
No me preocupa cómo solucionar este script, pero ¿cómo puedo evitar que el script continúe si encuentra este error? Pensé que set -e
haría cumplir este comportamiento.
Respuesta1
set -e
no se activa cuando fallan comandos que se usan como condiciones como en la sección de condiciones de // if
construcciones o a la izquierda de a , o en funciones, subcapas, archivos fuente, código ed que se invocan bajo esas condiciones.while
until
||
&&
eval
Si así fuera, entonces:
if [ ! -f /custom.log ]; then
Saldría del script si /custom.log
fuera un archivo normal y [
también saldría con un estado de salida distinto de cero.
El [
comando integrado del bash
shell (y la mayoría de las otras implementaciones) sale con un 1
estado si no se cumple la condición probada y 2
si hay un error de sintaxis (aunque no todos los errores de sintaxis, por ejemplo, no en [ -v 'a[+]' ]
).POSIX requiere que el estado de salida sea mayor que 1 en caso de error.
Por lo tanto, puede optar por salir del script si un comando sale con un código mayor que 1, independientemente de si se usa en una condición o no con algo como:
shopt -s extdebug # make sure the DEBUG trap propagates to subshells
trap '(($?>1 && (ret=$?))) && exit "$ret"' DEBUG
[ -f / ] || echo / not a regular file # OK
[ -f /] || echo was a syntax error # causes an exit, not output
echo not reached
Tenga en cuenta que no puede usar la ERR
trampa para eso, ya que la ERR
trampa solo se ejecuta en las mismas condiciones que las que activan la salida mediante set -e
.
Ahora, tenga cuidado con las implicaciones. Por ejemplo, eso causaría:
if grep -qs pattern /file; then
echo pattern was found in /file
fi
salir si /file
no existía o no era legible, ya que grep
regresa con estado 2 en ese caso, aunque con -s
, la intención era claramente ignorar esos casos.
Por lo tanto, deberá tener cuidado con las condiciones en las que los comandos que utiliza en sus condiciones pueden salir con un estado mayor que 1. Para solucionarlos, necesitaría algo como:
if sh -c 'grep -sq pattern / file || exit 1'; then...
Podrías restringir elsalir al estado de salida mayor que 1al comando [
o test
con algo como:
unset -v previous_BASH_COMMAND
trap '
case $previous_BASH_COMMAND in
("[ "* | "test "*) (($?>1 && (ret=$?))) && exit "$ret"
esac
previous_BASH_COMMAND=$BASH_COMMAND' DEBUG
Eso tiene algunas limitaciones. En
echo x
([ -f/]; echo y)
Eso haría que el subshell saliera, pero no el padre, ya que $previous_BASH_COMMAND
no se ha configurado allí. Y en:
[ -f / ] && echo a regular file
(grep -qs foo /file && echo foo in /file)
echo here
Se cerraría el shell al ejecutarlo echo here
, porque $?
sería 2 y $previous_BASH_COMMAND
era [ -f / ]
.
En cualquier caso, cosas como
[ -f /] | cat
export var="$([ -f /])"
no se pudo detectar porque el estado de salida no se propaga al proceso de shell principal (excepto con la pipefail
opción en el primer caso).
Ahora, no estoy seguro de que valga la pena agregar este tipo de detección (frágil) en tiempo de ejecución, cuando el error es fácilmente detectable en tiempo de desarrollo (cuando escribe y prueba su script).