Cómo hacer algo condicionalmente si un comando tuvo éxito o falló


286

¿Cómo puedo hacer algo así en bash?

if "`command` returns any error";
then
    echo "Returned an error"
else
    echo "Proceed..."
fi

Respuestas:


363

Cómo hacer algo condicionalmente si un comando tuvo éxito o falló

Eso es exactamente lo ifque hace la declaración de bash :

if command ; then
    echo "Command succeeded"
else
    echo "Command failed"
fi

Agregar información de los comentarios: no necesita utilizar la sintaxis [... ]en este caso. [es en sí un comando, casi equivalente a test. Es probablemente el comando más común para usar en un if, lo que puede llevar a suponer que es parte de la sintaxis del shell. Pero si desea probar si un comando tuvo éxito o no, use el comando directamente con if, como se muestra arriba.

Editar: Citó la pregunta en la parte superior para mayor claridad (esta respuesta no aparece cerca de la parte superior de la página).


11
Tenga en cuenta que el punto y coma es importante.
Thorbjørn Ravn Andersen

21
O simplemente puede poner thenen una línea separada.
l0b0

36
@ Joe: Creo que te refieres if ! command ; then ... ; fi. [es en sí mismo un comando, y no es necesario en este caso.
Keith Thompson el

20
@ Joe: Mi camino también tiene la virtud de ser correcto. if [ ! command ]no se ejecuta command; lo trata commandcomo una cadena y lo trata como verdadero porque tiene una longitud distinta de cero. [es un sinónimo del testcomando
Keith Thompson

55
Ups Estás en lo correcto.
Joe

133

Para cosas pequeñas que desea que sucedan si un comando de shell funciona, puede usar la &&construcción:

rm -rf somedir && trace_output "Removed the directory"

De manera similar, para cosas pequeñas que desea que sucedan cuando falla un comando de shell, puede usar ||:

rm -rf somedir || exit_on_error "Failed to remove the directory"

O ambos

rm -rf somedir && trace_output "Removed the directory" || exit_on_error "Failed to remove the directory"

Probablemente no sea prudente hacer mucho con estas construcciones, pero en ocasiones pueden hacer que el control sea mucho más claro.


3
Son más cortos y (al menos en algunas conchas) más rápidos. Me estremezco al recordar un script de instalación monstruoso de Ultrix escrito con estas construcciones condicionales que una vez intenté descifrar ...
vonbrand

104

Verifique el valor de $?, que contiene el resultado de ejecutar el comando / función más reciente:

#!/bin/bash

echo "this will work"
RESULT=$?
if [ $RESULT -eq 0 ]; then
  echo success
else
  echo failed
fi

if [ $RESULT == 0 ]; then
  echo success 2
else
  echo failed 2
fi

11
Si bien es técnicamente correcto (y por lo tanto no garantiza un voto negativo), no está haciendo uso del iflenguaje de Bash . Prefiero la respuesta de Keith Thompson.
Janmoesen

16
Hay beneficios en este idioma: conserva el valor de retorno. En general, creo que este es más poderoso, aunque más detallado. También es más fácil de leer.
Taxilian

2
¿Qué es "Bash's if idiom"?
Nowaker

3
@Nowaker El hecho de que el único propósito de esto ifes hacer esto. Todas las condiciones de control de flujo en Bash examinan $?detrás de escena; Eso es lo que hacen. Examinar explícitamente su valor debería ser innecesario en la gran mayoría de los casos, y generalmente es un antipatrón para principiantes.
tripleee el

1
@lunakid Si no te importa el código de salida, if ! cmdestá bien. De lo contrario, una disposición común es utilizar una elsecláusula. No puede tener un espacio vacío, thenpero a veces puede ver then : nothing; elsedónde el :no-op es significativo. truefuncionaría allí también.
tripleee

47

Esto funcionó para mí:

command && echo "OK" || echo "NOK"

si commandtiene éxito, entonces echo "OK"se ejecuta, y dado que tiene éxito, la ejecución se detiene allí. De lo contrario, &&se omite y echo "NOK"se ejecuta.


55
Si desea hacer algo si falla, y preservar el código de salida (para mostrar en el símbolo del sistema o probar en un script), puede hacer esto: command && echo "OK" || c=$?; echo "NOK"; $(exit $c)
Sam Hasler

2
@ Sam-Hasler: ¿no debería ser eso command && echo "OK" || (c=$?; echo "NOK"; (exit $c))?
jrw32982

99
Además, si la echo "OK"parte podría fallar, entonces esto es mejor:command && (echo "OK"; exit 0) || (c=$?; echo "NOK"; (exit $c))
jrw32982

@ jrw32982, Niza, he usado la primera construcción, pero no la segunda.
Sam Hasler

La verdadera respuesta está en los comentarios, ¡gracias a todos!
Joshua Pinter

6

Cabe señalar que if...then...fiy &&/ ||tipo de enfoque trata con el estado de salida devuelto por el comando que queremos probar (0 en caso de éxito); sin embargo, algunos comandos no devuelven un estado de salida distinto de cero si el comando falló o no pudo manejar la entrada. Esto significa que los enfoques habituales ify &&/ ||no funcionarán para esos comandos en particular.

Por ejemplo, en Linux, GNU filetodavía sale con 0 si recibió un archivo no existente como argumento y findno pudo ubicar el archivo especificado por el usuario.

$ find . -name "not_existing_file"                                          
$ echo $?
0
$ file ./not_existing_file                                                  
./not_existing_file: cannot open `./not_existing_file' (No such file or directory)
$ echo $?
0

En tales casos, una forma potencial de manejar la situación es mediante la lectura stderr/ stdinmensajes, por ejemplo, aquellos que regresaron por filecomando, o analizar la salida del comando como en find. Para ese propósito, se casepodría usar una declaración.

$ file ./doesntexist  | while IFS= read -r output; do                                                                                                                  
> case "$output" in 
> *"No such file or directory"*) printf "%s\n" "This will show up if failed";;
> *) printf "%s\n" "This will show up if succeeded" ;;
> esac
> done
This will show up if failed

$ find . -name "doesn'texist" | if ! read IFS= out; then echo "File not found"; fi                                                                                     
File not found

2

El más propenso a errores que pude encontrar fue:

  • Primero, obtenga el valor. Supongamos que haces algo como:

RR=$?

Ahora, no solo por esta situación, sino por otras que pueda enfrentar, considere:

variable definida:

$ AA=1 ; if (( "10#0${AA}" == 1 )) ; then echo yes ; else echo no ; fi

Respuesta: sí

$ AA=1 ; if (( "10#0${AA}" != 1 )) ; then echo yes ; else echo no ; fi

Respuesta: no

variable indefinida:

$ AA=1 ; if (( "10#0${BB}" == 1 )) ; then echo yes ; else echo no ; fi

Respuesta: no

$ AA=1 ; if (( "10#0${BB}" != 1 )) ; then echo yes ; else echo no ; fi

Respuesta: sí

$ AA=1 ; if (( "10#0${BB}" == 0 )) ; then echo yes ; else echo no ; fi

Respuesta: sí

Esto evita todo tipo de errores.

Probablemente conozca toda la sintaxis, pero aquí hay algunos consejos:

  • Usa comillas. Evita un "blank"ser nothing.
  • La nueva notación moderna para variables es ${variable}.
  • Agregar un cero concatenado antes de su número también evita "ningún número en absoluto".
  • Pero espera, agregar un cero hace que el número se convierta base-8. Recibirá un error como:
    • value too great for base (error token is "08")para los números de arriba 7. Es entonces cuando 10#entra en juego:
    • 10#obliga al número a ser base-10.

1

Puedes hacerlo:

if ($( ping 4.4.4.4 -c1 > /dev/null )) ; then
  echo "ping response succsess!!!"
fi

99
Eso funciona pero es complicado. Está ejecutando ping en una subshell de una subshell, la salida de pingse captura en vista de ejecutarlo como un comando. Pero debido a que la salida se redirige a / dev / null, esa siempre será la cadena vacía. Por lo tanto, no está ejecutando nada en una subshell, lo que significa que se mantendrá el estado de salida anterior (de la subshell de sustitución de comandos, que es de ping). Obviamente, la forma correcta es if ping ...; thenaquí.
Stéphane Chazelas 01 de

1

Como se señaló en otra parte de este hilo, la pregunta original básicamente se responde a sí misma. Aquí hay una ilustración que muestra que las ifcondiciones también pueden estar anidadas.

Este ejemplo se utiliza ifpara verificar si existe un archivo y si es un archivo normal. Si esas condiciones son verdaderas, compruebe si tiene o no un tamaño mayor que 0.

#!/bin/bash

echo "Which error log are you checking today? "
read answer

if [ -f /opt/logs/$answer*.errors ]
    then
        if [ -s /opt/logs/$answer*.errors ]
            then
                echo "Content is present in the $answer error log file."
            else
                echo "No errors are present in the $answer error log file."
        fi
    else
        echo "$answer does not have an error log at this time."
fi

2
Eso es lo que hace su respuesta, pero su respuesta no responde a la pregunta.
Jeff Schaller

@ JeffSchaller, gracias por tu nota. Edité mi publicación para incluir una referencia a la pregunta.
quartzinquartz

1
#!/bin/bash

if command-1 ; then
   echo "command-1 succeeded and now running from this block command-2"
   command-2
else
   echo "command-1 failed and now running from this block command-3"
   command-3
fi

3
Esto se parece bastante a la respuesta aceptada existente ; Por favor, atribuir el trabajo vuelve a utilizar, o añadir su propio esfuerzo en sus respuestas.
Jeff Schaller

-3

Esto podría hacerse simplemente de esta manera, ya que $?le da el estado del último comando ejecutado.

Entonces podría ser

#!/bin/sh

... some command ...

if [ $? == 0 ] ; then
  echo '<the output message you want to display>'
else 
  echo '<failure message>'
fi

1
Voto negativo: Esto simplemente parafrasea una respuesta anterior que legítimamente recibió críticas por ser unidiomático.
tripleee el
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.