Códigos de Saída: Usando $? e && ||

Códigos de Saída: Usando $? e && ||

O que você vai conseguir fazer

  • Verificar se o último comando teve sucesso ou falhou com `echo $?`
  • Escrever as ramificações "se der certo" e "se falhar" com `&&` e `||`
  • Informar sucesso ou falha a quem chamou usando `exit 0` e `exit 1`

Pré-requisitos (leia estes primeiro)

O Que Você Vai Aprender

As palavras usadas aqui

  • Código de saída: o número que um comando retorna ao terminar. Exit code, exit status e código de retorno nomeiam a mesma coisa.
  • Variável: uma caixa com nome que guarda um valor. O $? é uma variável especial que o shell preenche sozinho.
  • Shell script: um arquivo com comandos escritos em ordem.

Todos os comandos usados aqui -- ls, echo, cd, mkdir -- deixam os seus arquivos existentes em paz, então experimente à vontade.

Resumo Rápido

  • Todo comando retorna um número quando termina (0 significa sucesso, qualquer outra coisa significa falha)
  • Quer ver o último resultado? echo $?
  • Executar próximo em caso de sucesso -> &&
  • Executar próximo em caso de falha -> ||

1. O Que é um Código de Saída?

Conclusão: Um código de saída é o número que um comando retorna. 0 significa sucesso; 1 a 255 significam falha.

Lina: Senpai, quando eu executo um comando, a saída aparece na tela. Mas como eu sei se ele teve sucesso ou falhou?
Veterano Linny: Boa pergunta. Todo comando retorna um número quando termina. Chamamos isso de código de saída.
Lina: Um número? Eu não vejo nenhum na tela.
Veterano Linny: Ele fica oculto da visualização normal. Pense em um comprovante de entrega. Separado da encomenda, fica um registro dizendo "entregue" ou "ninguém em casa".
Veterano Linny: O comando faz o mesmo. Além da saída, ele deixa um registro. A regra é simples: 0 significa sucesso, qualquer outra coisa significa falha.

A regra básica

Número Significado
0 Sucesso
1-255 Falha (algum problema)

"0 significa sucesso" contraria a intuição. Fixe isso primeiro.

2. Por Que o Código de Saída Importa?

Conclusão: O código de saída é o que permite automatizar "executar o próximo passo apenas quando este teve sucesso."

Lina: Certo, um número é retornado. Mas para que ele serve?
Veterano Linny: Imagine uma tarefa como "apagar os arquivos antigos apenas depois que o backup der certo". Apagá-los após um backup falho seria um desastre.
Lina: Seria mesmo. Eu queria que a próxima ação dependesse de a primeira ter funcionado.
Veterano Linny: Exatamente. Essa decisão depende do código de saída. É a base da automação e do shell scripting.

Julgar o sucesso apenas pelo que é impresso na tela é arriscado. Um comando pode falhar mesmo quando nenhum texto "Error" aparece.

Confiar no código de saída, que a máquina consegue ler, é bem mais seguro.

3. Verificar o Resultado com $?

Conclusão: $? contém o código de saída do último comando. Use echo $? para vê-lo.

O código de saída do comando que você acabou de executar fica em uma variável especial chamada $?. O shell a preenche sozinho.

$ ls /etc
hosts  passwd  ...
$ echo $?
0
Lina: Recebi 0! Então ls teve sucesso.
Veterano Linny: Certo. Agora vamos fazê-lo falhar de propósito. Tente executar ls em um caminhoUma sequência de texto que descreve a localização de um arquivo ou diretório. que não existe.
$ ls /not-exist
ls: cannot access '/not-exist': No such file or directory
$ echo $?
2
Lina: Dessa vez recebi 2. É um número diferente do anterior.
Veterano Linny: Em caso de falha, ele retorna algo diferente de 0. O ls retorna 2 para o erro de "arquivo não encontrado".
Veterano Linny: Você não precisa decorar os números exatos. Basta olhar se é 0 ou não.

O $? guarda apenas o resultado do comando imediatamente anterior.

Execute echo $? duas vezes seguidas e o segundo reporta o primeiro echo. Aquele teve sucesso, então você recebe 0. Verifique apenas uma vez.

4. Encadear "em caso de sucesso" com &&

Conclusão: cmd1 && cmd2 executa cmd2 apenas quando cmd1 tem sucesso (retorna 0).

O && é o símbolo que significa "se o lado esquerdo tiver sucesso, execute o lado direito também".

$ mkdir backup && cp data.txt backup/
Lina: Então isso significa "executar o cp apenas se o mkdir tiver sucesso"?
Veterano Linny: Exatamente. Se o mkdir backup falhar, o cp não executa. Isso acontece quando você não tem permissãoAs regras de acesso de leitura / escrita / execução definidas em um arquivo ou diretório. para criar o diretórioUm local que organiza arquivos. É a mesma ideia da "pasta" do Windows ou macOS., por exemplo.
Veterano Linny: Use quando um passo depende do anterior ter dado certo.
# Executar testes apenas se o build tiver sucesso
$ make && make test

# Listar o conteudo apenas se o cd tiver sucesso
$ cd /var/log && ls

Pense no && como "prosseguir quando a pré-condição for atendida". Ele encadeia passos com segurança quando você quer parar assim que algo falhar.

5. Encadear "em caso de falha" com ||

Conclusão: cmd1 || cmd2 executa cmd2 apenas quando cmd1 falha. Use para tratamento de erros.

O || é o oposto do &&. Ele significa "se o lado esquerdo falhar, execute o lado direito".

$ cd /var/log || echo "Nao foi possivel entrar no diretorio"
Lina: Então, se o cd falhar, ele imprime uma mensagem. E quando tem sucesso, nada é impresso?
Veterano Linny: Correto. Se o cd tiver sucesso, o echo nunca executa. O || costuma servir como uma rede de segurança para quando as coisas dão errado.
# Sugerir instalacao se um comando estiver faltando
$ which jq || echo "jq nao esta instalado. Por favor, instale-o."

# Sair do script se o passo falhar
$ cp data.txt backup/ || exit 1

&& vs || resumo visual

cmd1 && cmd2   ->  cmd2 executa se cmd1 [tiver sucesso]
cmd1 || cmd2   ->  cmd2 executa se cmd1 [falhar]

Lembre assim: "&& prossegue em caso de sucesso" e "|| prossegue em caso de falha".

6. Combinando && e ||

Conclusão: cmd && em-sucesso || em-falha escreve "faça A em caso de sucesso, B em caso de falha" em uma linha.

Encadear && e || permite agir de um jeito em caso de sucesso e de outro em caso de falha.

$ ping -c1 example.com > /dev/null && echo "Conexao OK" || echo "Conexao NG"
Lina: Então eu posso escrever "imprimir OK em caso de sucesso, NG em caso de falha" em uma única linha.
Veterano Linny: Sim, mas há uma ressalva nesse padrão.
Veterano Linny: Se a própria ação de sucesso falhar -- ou seja, o echo "Conexao OK" -- a ação de falha executa também.
Veterano Linny: Isso é aceitável para mensagens simples. Para algo complexo, um if é mais seguro.

A && B || C não é o mesmo que if A then B else C. Se B falhar, C também executa.

Quando você precisa de uma ramificação confiável, use uma instrução if. O artigo da próxima leitura explica como.

7. Armadilhas Comuns de Iniciantes

Conclusão: Os erros fáceis são inverter "0 significa sucesso" e ler $? tarde demais.

7-1. Lembrar "0 significa sucesso" ao contrário

Lina: Honestamente, "0 significa sucesso" ainda parece estranho para mim.
Veterano Linny: Todo mundo sente isso. Pense assim: "0 = sem erros = zero problemas". Assim fixa.
Veterano Linny: Imagine uma contagem de erros, e não uma nota de prova.

7-2. Lina se atrapalha: ler o $? tarde demais

$ ls /not-exist
$ pwd            # <- um comando extra escapou
$ echo $?        # isto mostra o resultado de pwd (0, sucesso)
Lina: O comando com certeza falhou, mas recebi 0. O código de saída está quebrado?
Veterano Linny: Nada está quebrado. O $? só lembra de um comando para trás.
Lina: Então o pwd que entrou no meio substituiu o valor?
Veterano Linny: Isso. O pwd tem sucesso, então o valor virou 0. O resultado do ls já tinha sido sobrescrito.
Lina: O resultado que eu queria tinha sumido. Entendi. Vou ler logo em seguida a partir de agora.

Verifique o $? logo após o comando que você quer conferir. Qualquer comando entre eles sobrescreve o valor.

7-3. Retornando um código de saída você mesmo

Quando você quer que um script informe sucesso ou falha a quem o chamou, use exit.

exit 0   # encerrar como sucesso
exit 1   # encerrar como falha

Se você chamar exit sem um número, ele retorna o código de saída do último comando como está.

8. Exercícios Práticos

Conclusão: Três exercícios — verificar, executar em sucesso, executar em falha — para praticar $?, && e ||.

Lina: Entendi os conceitos. Quero tentar eu mesma.
Veterano Linny: Ótimo. Preparei três exercícios. Execute-os no seu terminal.

Exercício 1: Execute um comando e, logo em seguida, mostre o código de saída dele.

Ver Dica 1 (Direção)

O último resultado fica em uma variável especial que o shell preenche. Mostre o conteúdo dela na tela.

Ver Dica 2 (Nome do comando)

A variável é $?. Mostre com echo.

Ver Resposta
$ ls
$ echo $?
0

O ls teve sucesso, então você recebe 0. Aponte para um diretório que não existe e o número muda para algo diferente de 0.

Exercício 2: Mostre "Criado" apenas quando o mkdir test-dir tiver sucesso.

Ver Dica 1 (Direção)

Junte dois comandos em uma linha. Use o símbolo que executa o lado direito somente quando o esquerdo tem sucesso.

Ver Dica 2 (Nome do comando)

O símbolo é &&. Mostre com echo.

Ver Resposta
$ mkdir test-dir && echo "Criado"
Criado

Execute a mesma linha uma segunda vez e o mkdir falha, então "Criado" não aparece. Remova o diretório depois com rmdir test-dir.

Exercício 3: Tente fazer cd em um diretório que não existe e mostre "Não foi possível mover" apenas quando falhar.

Ver Dica 1 (Direção)

Mesmo formato do Exercício 2, com o símbolo invertido. Use o que executa o lado direito somente quando o esquerdo falha.

Ver Dica 2 (Nome do comando)

O símbolo é ||. Para mover, use cd.

Ver Resposta
$ cd /not-exist || echo "Nao foi possivel mover"
bash: cd: /not-exist: No such file or directory
Nao foi possivel mover

Você vê tanto a mensagem de erro do próprio cd quanto a mensagem que você mandou imprimir.

9. Revisão

Lina: Vou resumir. Um comando retorna um número ao terminar. 0 é sucesso, qualquer outra coisa é falha.
Veterano Linny: Isso. Você lê esse número com echo $?, mas só o do comando imediatamente anterior.
Lina: E o && prossegue em caso de sucesso, enquanto o || prossegue em caso de falha. Anotado.
Veterano Linny: É isso. Agora experimente terminar um script seu com exit 0 e exit 1.

10. Modelos para Copiar e Colar

Conclusão: Mantenha os padrões comuns — verificar, em sucesso, em falha, ramificar e sair — ao alcance.

Padrões comuns para ter à mão

# Verificar o ultimo codigo de saida
echo $?

# Executar proximo apenas em caso de sucesso
commandA && commandB

# Executar proximo apenas em caso de falha
commandA || commandB

# A em caso de sucesso, B em caso de falha (ramificacao simples)
command && echo "OK" || echo "NG"

# Sair do script se o passo falhar
command || exit 1

# Encerrar um script explicitamente como sucesso / falha
exit 0   # sucesso
exit 1   # falha

Resumo do Dia em 3 Linhas

  1. Um comando retorna um número ao terminar. 0 é sucesso, qualquer outra coisa é falha
  2. Leia o último resultado com echo $?, sem nenhum comando no meio
  3. O && prossegue em caso de sucesso. O || prossegue em caso de falha

Próxima Leitura