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.