Bash ignorando el error de un comando en particular

444

Estoy usando las siguientes opciones

set -o pipefail
set -e

En script bash para detener la ejecución por error. Tengo ~ 100 líneas de ejecución de script y no quiero verificar el código de retorno de cada línea en el script.

Pero para un comando en particular, quiero ignorar el error. ¿Cómo puedo hacer eso?

Vivek Goel
fuente

Respuestas:

757

La solución:

particular_script || true

Ejemplo:

$ cat /tmp/1.sh
particular_script()
{
    false
}

set -e

echo one
particular_script || true
echo two
particular_script
echo three

$ bash /tmp/1.sh
one
two

three nunca se imprimirá

Además, quiero agregar que cuando pipefailestá activado, es suficiente que Shell piense que toda la tubería tiene un código de salida distinto de cero cuando uno de los comandos en la tubería tiene un código de salida distinto de cero (con pipefailoff debe ser el último) .

$ set -o pipefail
$ false | true ; echo $?
1
$ set +o pipefail
$ false | true ; echo $?
0
Igor Chubin
fuente
15
+1. Como explica el Manual de referencia de Bash , "El shell no sale" cuando -ese establece el atributo "si el comando que falla es parte de la lista de comandos inmediatamente después de una palabra clave whileo until, parte de la prueba en una ifdeclaración, parte de cualquier comando ejecutado "en una lista &&o ||excepto el comando que sigue al final &&o ||, cualquier comando en una tubería pero el último, o si el estado de retorno del comando se está invirtiendo !".
ruakh
@IgorChubin No sé por qué, pero esto no funciona output = (ldd $2/bin/* || true) | grep "not found" | wc -lscript termina después de esta línea cuando ldd devuelve el error
Vivek Goel
1
(ldd $2/bin/* || true) | grep "not found" | wc -l || true
Igor Chubin
1
debido set -o pipefail. cuando grepno encuentra nada, devuelve un código de salida distinto de cero y es suficiente para que Shell piense que toda la tubería tiene un código de salida distinto de cero.
Igor Chubin
3
Si desea la opción de preservar el valor de retorno (sin salir), pruebe mycommand && true. Esto le permite verificar el código de retorno en los pasos posteriores y manejarlo mediante programación.
Ed Ost
178

Simplemente agregue || truedespués del comando donde desea ignorar el error.

Lars Kotthoff
fuente
11
Creo que es importante agregar esto: este método permitirá que el código de respuesta y el mensaje de error persistan, mientras que el "!" El método descrito a continuación cambiará el código de respuesta y, por lo tanto, no generará el error. Esto es importante cuando se usa set -ee intenta capturar un mensaje de error: por ejemploset -e; TEST=$(foo 2>&1 || true); echo $TEST
Spanky el
87

No pare y también guarde el estado de salida

En caso de que si desea que su script no se detenga si falla un comando en particular y también desea guardar el código de error del comando fallido:

set -e
EXIT_CODE=0
command || EXIT_CODE=$?
echo $EXIT_CODE
Arslan Qadeer
fuente
2
Esto es exactamente lo que necesitaba
sarink
no funciona para mí por alguna razón ... oh bueno
Jeef
EXIT_CODE no se establece en cero cuando el comando devuelve 0. Sin embargo, se capturan códigos de salida distintos de cero. ¿Alguna idea de por qué?
Ankita13
@ Ankita13 Porque si fue cero, el primero commandfue exitoso y es necesario ejecutar lo que sigue ||. léelo como: si commandfalla, hazlo EXIT_CODE=$?. Probablemente podría hacer command || echo "$?"o usar "trap" para una depuración más detallada. Vea esto -> stackoverflow.com/a/6110446/10737630
tinnick el
63

Más concisamente:

! particular_script

De la especificación POSIX con respecto set -e(énfasis mío):

Cuando esta opción está activada, si un comando simple falla por alguno de los motivos enumerados en Consecuencias de errores de shell o devuelve un valor de estado de salida> 0, y no forma parte de la lista compuesta después de un tiempo, hasta, o si es una palabra clave, y no es parte de una lista AND u OR, y no es una tubería precedida por! palabra reservada , entonces el caparazón saldrá inmediatamente.

Lily Finley
fuente
16
Esto solo invierte el código de salida de un comando, por lo que el comando que finalizó correctamente devolverá 1 en lugar de 0 y fallará el script con -e.
Marboni
17
Tengo entendido que !evitará que el shell salga sin importar qué. Este script muestra el mensaje Still alive!cuando lo ejecuto, indicando que el script se ejecutó hasta su finalización. ¿Estás viendo un comportamiento diferente?
Lily Finley
77
tiene razón, invierte el estado de salida, pero no bloquea el script cuando el comando termina con 0 o 1. Estuve desatento. De todos modos, gracias por citar la documentación, puse mi expresión en ifcláusula y resolví el problema.
Marboni
42

En lugar de "devolver verdadero", también puede usar la utilidad "noop" o nula (como se menciona en las especificaciones POSIX ) :y simplemente "no hacer nada". Ahorrarás algunas cartas. :)

#!/usr/bin/env bash
set -e
man nonexistentghing || :
echo "It's ok.."
Timo
fuente
3
aunque ||: no es tan claro como || cierto (que puede confundir a los usuarios no expertos), me gusta la concisión
David
Esta variante no devuelve ningún texto que pueda ser importante en CI.
kivagant
5

Si desea evitar que su script falle y recopile el código de retorno:

command () {
    return 1  # or 0 for success
}

set -e

command && returncode=$? || returncode=$?
echo $returncode

returncode se recopila sin importar si el comando tiene éxito o falla.

volingas
fuente
2

He estado usando el fragmento a continuación cuando trabajo con herramientas de CLI y quiero saber si existe algún recurso o no, pero no me importa el resultado.

if [ -z "$(cat no_exist 2>&1 >/dev/null)" ]; then
    echo "none exist actually exist!"
fi
Payman
fuente
1
Esta parece ser una manera indirecta y moderadamente costosa de decirif [ -r not_exist ]
tripleee el
1

Me gusta esta solución:

: `particular_script`

El comando / script entre los ticks posteriores se ejecuta y su salida se alimenta al comando ":" (que es el equivalente de "verdadero")

$ false
$ echo $?
1
$ : `false`
$ echo $?
0

editar: Corregido error feo

Q-life
fuente
0

mientras que || truese prefiere uno, pero también puedes hacer

var=$(echo $(exit 1)) # it shouldn't fail
Foto Blysk
fuente
0

No hay soluciones que funcionen para mí desde aquí, así que encontré otra:

set +e
find "./csharp/Platform.$REPOSITORY_NAME/obj" -type f -iname "*.cs" -delete
find "./csharp/Platform.$REPOSITORY_NAME.Tests/obj" -type f -iname "*.cs" -delete
set -e

Esto es útil para CI y CD. De esta forma, se imprimen los mensajes de error pero todo el script continúa ejecutándose.

Konard
fuente
0
output=$(*command* 2>&1) && exit_status=$? || exit_status=$?
echo $output
echo $exit_status

Ejemplo de uso de esto para crear un archivo de registro

log_event(){
timestamp=$(date '+%D %T') #mm/dd/yy HH:MM:SS
echo -e "($timestamp) $event" >> "$log_file"
}

output=$(*command* 2>&1) && exit_status=$? || exit_status=$?

if [ "$exit_status" = 0 ]
    then
        event="$output"
        log_event
    else
        event="ERROR $output"
        log_event
fi
Robert.C
fuente
0

Gracias por la solución simple aquí desde arriba:

<particular_script/command> || true

La siguiente construcción podría usarse para acciones adicionales / resolución de problemas de pasos de guión y opciones adicionales de control de flujo:

if <particular_script/command>
then
   echo "<particular_script/command> is fine!"
else
   echo "<particular_script/command> failed!"
   #exit 1
fi

Podemos frenar las acciones adicionales y exit 1si es necesario.

Almaz Gareev
fuente