"set -e" no finaliza el script cuando se produce un error en condicional

"set -e" no finaliza el script cuando se produce un error en condicional

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 -eharía cumplir este comportamiento.

Respuesta1

set -eno se activa cuando fallan comandos que se usan como condiciones como en la sección de condiciones de // ifconstrucciones o a la izquierda de a , o en funciones, subcapas, archivos fuente, código ed que se invocan bajo esas condiciones.whileuntil||&&eval

Si así fuera, entonces:

if [ ! -f /custom.log ]; then

Saldría del script si /custom.logfuera un archivo normal y [también saldría con un estado de salida distinto de cero.

El [comando integrado del bashshell (y la mayoría de las otras implementaciones) sale con un 1estado si no se cumple la condición probada y 2si 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 ERRtrampa para eso, ya que la ERRtrampa 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 /fileno existía o no era legible, ya que grepregresa 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 testcon 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_COMMANDno 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_COMMANDera [ -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 pipefailopció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).

información relacionada