Como fazer algo condicionalmente se um comando tiver êxito ou falha


283

Como posso fazer algo assim no bash?

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

Respostas:


359

Como fazer algo condicionalmente se um comando tiver êxito ou falha

É exatamente isso que a ifdeclaração do bash faz:

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

Adicionando informações de comentários: você não precisa usar a [... ]sintaxe neste caso. [é em si um comando, quase equivalente a test. É provavelmente o comando mais comum a ser usado em um arquivo if, o que pode levar à suposição de que ele faz parte da sintaxe do shell. Mas se você quiser testar se um comando foi bem-sucedido ou não, use o próprio comando diretamente com if, como mostrado acima.

Editar: citou a pergunta na parte superior para maior clareza (esta resposta não aparece na parte superior da página).


11
Observe que o ponto e vírgula é importante.
— Thorbjørn Ravn Andersen

21
Ou você pode simplesmente colocar thenem uma linha separada.
— l0b0 17/10/11

36
@ Joe: Eu acho que você quer dizer if ! command ; then ... ; fi. [é em si um comando e não é necessário neste caso.
— Keith Thompson

20
@ Joe: Meu caminho também tem a virtude de estar correto. if [ ! command ]não executa command; ele trata commandcomo uma string e o trata como verdadeiro porque tem um comprimento diferente de zero. [é um sinônimo para o testcomando
— Keith Thompson

5
Opa Você está certo.
— Joe

133

Para pequenas coisas que você deseja que ocorram se um comando shell funcionar, você pode usar a &&construção:

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

Da mesma forma, para pequenas coisas que você deseja que ocorram quando um comando do shell falha, você pode usar ||:

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

Ou ambos

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

Provavelmente, não é aconselhável fazer muito com essas construções, mas elas podem ocasionalmente tornar o fluxo de controle muito mais claro.


3
Eles são mais curtos e (pelo menos em algumas conchas) mais rápidos. Tremo só lembrando um script monstro instalação Ultrix escrito com apenas estas construções condicionais uma vez que eu tentava decifrar ...
— vonbrand

101

Verifique o valor de $?, que contém o resultado da execução do comando / função mais recente:

#!/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
Embora tecnicamente correto (e, portanto, não justifique um voto negativo), ele não está usando o ifidioma de Bash . Eu prefiro a resposta de Keith Thompson.
— janmoesen 17/10/11

16
Há benefícios para esse idioma - ele preserva o valor de retorno. Ao todo, acho que este é mais poderoso, embora mais detalhado. também é mais fácil de ler.
— Taxilian # 7/15

2
O que é "Bash's if idiom"?
— Nowaker

3
@Nowaker O fato de que o único objetivo ifé fazer isso. Todas as condições de controle de fluxo no Bash são examinadas $?nos bastidores; é isso que eles fazem. O exame explícito de seu valor deve ser desnecessário na grande maioria dos casos, e geralmente é um antipadrão para iniciantes.
— Tripleee

1
@ lunakid Se você não se importa com o código de saída, tudo if ! cmdbem. Caso contrário, um acordo comum é usar uma elsecláusula. Você não pode ter um vazio, thenmas às vezes vê then : nothing; elseonde o :no-op é significativo. trueiria trabalhar lá também.
— tripleee 19/08

47

Isso funcionou para mim:

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

se commandfor bem-sucedido, echo "OK"é executado e, como é bem-sucedido, a execução pára por aí. Caso contrário, &&é ignorado e echo "NOK"é executado.


5
Se você quer fazer algo se ele falhar, e preservar o código de saída (para mostrar no prompt de comando ou teste em um script), você pode fazer isso: command && echo "OK" || c=$?; echo "NOK"; $(exit $c)
— Sam Hasler

2
@ Sam-Hasler: não deveria ser command && echo "OK" || (c=$?; echo "NOK"; (exit $c))?
— Jrw32982

9
Além disso, se a echo "OK"parte em si poderia falhar, então isso é melhor:command && (echo "OK"; exit 0) || (c=$?; echo "NOK"; (exit $c))
— jrw32982

@ jrw32982, Nice, usei a construção anterior, mas não a última.
— Sam Hasler

A verdadeira resposta está nos comentários, obrigado a todos!
— Joshua Pinter

6

Deve-se notar que if...then...fie &&/ ||tipo de abordagem lida com o status de saída retornado pelo comando que queremos testar (0 em caso de sucesso); no entanto, alguns comandos não retornam um status de saída diferente de zero se o comando falhar ou não puder lidar com a entrada. Isso significa que as abordagens usuais ife &&/ ||não funcionarão para esses comandos específicos.

Por exemplo, no Linux, o GNU fileainda sai com 0 se recebeu um arquivo não existente como argumento e findnão conseguiu localizar o usuário especificado.

$ 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

Nesses casos, uma maneira potencial de lidarmos com a situação é lendo stderr/ stdinmensagens, por exemplo, aquelas que retornaram por filecomando ou analisam a saída do comando como em find. Para esse propósito, a caseinstrução pode ser usada.

$ 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

O mais propenso a erros que eu poderia ter foi:

  • Primeiro, obtenha o valor. Suponha que você faça algo como:

RR=$?

Agora, não apenas para esta situação, mas para outras que você possa enfrentar, considere:

variável definida:

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

Resposta: sim

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

Resposta: não

Variável indefinida:

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

Resposta: não

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

Resposta: sim

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

Resposta: sim

Isso evita todos os tipos de erros.

Você provavelmente conhece toda a sintaxe, mas aqui estão algumas dicas:

  • Use aspas. Evite "blank"ser nothing.
  • A nova notação moderna para variáveis ​​é ${variable}.
  • Adicionar um zero concatenado antes do seu número também evita "nenhum número".
  • Mas espere, adicionar um zero faz o número se tornar base-8. Você receberá um erro como:
    • value too great for base (error token is "08")para números acima 7. É quando 10#entra em jogo:
    • 10#força o número a ser base-10.

1

Você consegue fazer isso:

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

9
Isso funciona, mas é complicado. Você está executando ping em um subshell de um subshell, a saída de pingé capturada em vista de executá-lo como um comando. Mas como a saída é redirecionada para / dev / null, essa sempre será a string vazia. Portanto, você não está executando nada em um subshell, o que significa que o status de saída anterior (do subshell de substituição de comando, que é ping) será mantido. Obviamente, a maneira correta está if ping ...; thenaqui.
— Stéphane Chazelas 01/04/2015

1

Como observado em outra parte deste segmento, a pergunta original basicamente se responde. Aqui está uma ilustração mostrando que as ifcondições também podem ser aninhadas.

Este exemplo usa ifpara verificar se um arquivo existe e se é um arquivo regular. Se essas condições forem verdadeiras, verifique se ele tem ou não um tamanho maior 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
É isso que sua resposta faz, mas sua resposta não aborda a questão.
— Jeff Schaller

@JeffSchaller, obrigado por sua nota. Editei minha postagem para incluir uma referência à pergunta.
— 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
Isso se parece bastante com a resposta aceita existente ; por favor atribuem trabalho que você está reutilizando, ou adicione seu próprio esforço em suas respostas.
— Jeff Schaller

-3

Isso pode ser feito simplesmente desta maneira, pois $?fornece o status do último comando executado.

Então poderia ser

#!/bin/sh

... some command ...

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

1
Voto a favor: Isto simplesmente parafraseia uma resposta anterior que recebeu justamente críticas por ser unidiomatic.
— Tripleee
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.