¿Cómo detener el script bash cuando falla una condición?

11

Aquí se muestra el uso de ||y &&en una sola línea para concatenar la ejecución de comandos: ¿Cómo puedo verificar si hay errores de apt-get en un script bash?

Estoy tratando de detener la ejecución de un script si falla una determinada condición,

p.ej

false || echo "Obvious error because its false on left" && exit

Aquí imprime Obvious error because its false on the lefty sale de la consola, que es lo que quería.

true || echo "This shouldn't print" && exit

Aquí, no hay impresión de eco, pero el exitcomando también se ejecuta, es decir, la consola está cerrada, ¿no debería exitejecutarse el comando porque el comando de eco de la derecha no se ejecutó? ¿O es por defecto una declaración considerada falsa en el lado izquierdo de un &&operador?

Editar: debería haberlo mencionado antes, mi objetivo era hacer eco del error y salir si no estaba claro. wrt a mi caso específico de errores de captura al agrupar condiciones usando && y ||, @ bodhi.zazen respuesta resuelve el problema

La respuesta de @takatakatek deja más claro el control de flujo y los enlaces de la guía bash son excelentes

La respuesta de @muru tiene una buena explicación de por qué no usar set -e si desea que se generen mensajes de error personalizados con alternativas de uso de perl y trap, lo que creo que es una forma más sólida y me veo usándolo desde mi segundo script de bash en adelante.

kosol
fuente
Por lo que puedo decir, sale cuando se completa el guión.
Panther

Respuestas:

10

Probablemente quieras (como señaló Steeldriver)

true || { echo "This shouldn't print" && exit; }

De lo contrario, su script está leyendo uno condicional a la vez

aob y luego salir

Creo que quieres a o (b y salir)?

Pantera
fuente
44
En realidad, debe usar { ... ; }(como sugirió @steeldriver), de lo contrario, el exitúnico sale del subshell y el shell principal continúa ejecutando el script.
Gordon Davisson
14

El problema es que || y && solo omite una estrofa posterior de una cadena de comandos cuando la condición falla. Si escribe una estructura de bloque completo, tiene mucho sentido. Lo que escribiste se convierte en esto:

if ! true ; then
   echo "This shouldn't print"
else
   exit
fi

Lo que quieres es esto:

if ! true ; then
   echo "This shouldn't print"
   exit
fi

Cuando utiliza los operadores condicionales de acceso directo de Bash, es mejor evitar mezclarlos. La lógica es mucho más fácil de entender mientras la escribe, y sus lectores apreciarán su buen estilo.

takatakatek
fuente
2
Sí, en mi opinión, esto es mucho más cómo se debe escribir el condicionamiento en bash
derHugo
@takatakatek Esto deja más claro por qué falla en mi caso. Estoy de acuerdo en que la lógica es clara en este caso, pero no es la razón para agrupar condiciones usando && y || es evitar expresarlos usando declaraciones condicionales?
kosol
@kosol Sí, && y || combine dos cosas: probar el estado de salida del comando anterior y especificar un solo comando (o grupo de comandos) para ejecutar (para &&) u omitir (para ||). Pueden ser útiles para casos muy simples, pero no pueden reemplazar if ... then ... elif ... else ... fibloques completamente expresados . Sospecho que es posible que no sepa que Bash no tiene tipos booleanos verdaderos vistos en lenguajes más sofisticados. Si el estado de salida de un comando es 0 (cero), Bash lo trata como verdadero / exitoso . Si el estado de salida no es cero, Bash lo trata como falso / error .
takatakatek
7

La forma más sencilla de fallar en caso de error es usar la -eopción de bash ( set -eo /bin/bash -een el shebang). Sin embargo, esto no facilita el envío de mensajes de error personalizados. Hay un par de expresiones idiomáticas que podrían simplificarlo:

die à la Perl

Perl tiene un diecomando muy conveniente que imprime un mensaje en stderr y genera una excepción, que generalmente lleva a que finalice el script. Puedes emular esto:

die () {
  ret=$?
  print "%s\n" "$@" >&2
  exit "$ret"
}

false || die "Obvious error because its false on left"
true || die "This shouldn't print"

trap ERR

El trap <command>comando se puede usar para ejecutar un comando cuando se recibe una señal, o al salir del script ( trap ... EXIT), o cuando un comando devuelve un error ( trap ... ERR). Entonces algo como:

trap 'ret=$?; printf "%s\n" "$ERR_MSG" >&2; exit "$ret"' ERR

Entonces:

ERR_MSG="Obvious error because its false here"
false
ERR_MSG="This shouldn't print"
true

En cualquier caso, sugeriría usar al menos exit 1, para indicar que el script falló . Un plano exitutilizará el estado de salida del comando anterior, que en estos casos serían los comandos echoo printf, que generalmente tendrían éxito. Guardar el estado de salida del comando que falla como lo he hecho aquí sería algo bueno.

muru
fuente
6

Podría intentar experimentar un poco comenzando el script con

#!/bin/bash -e

El -e asegura que el script salga en el momento en que algo regrese falso. Una falla específica se puede evitar consomething || true

agregado 1166877
fuente