Em scripts Bash, todo comando executado retorna um código numérico ao finalizar, chamado de código de saída (ou exit status). Este código é fundamental para controle de fluxo, tratamento de erros e automação. Nesta aula, exploraremos como acessar, definir e utilizar esses códigos para criar scripts mais robustos e confiáveis.

O código de saída é um inteiro entre 0 e 255. Por convenção, o valor 0 indica sucesso, enquanto qualquer valor diferente de zero indica algum tipo de falha. O shell armazena o código do último comando executado em uma variável especial $?, permitindo que você tome decisões baseadas no resultado.

$?

A variável $? contém o código de saída do último comando executado em primeiro plano. Ela é atualizada a cada comando, então você deve consultá-la imediatamente após o comando de interesse, antes de executar outro.

Exemplo prático:

#!/bin/bash
ls /etc/passwd
echo "Código de saída: $?"
ls /arquivo_inexistente
echo "Código de saída: $?"

Saída esperada:

/etc/passwd
Código de saída: 0
ls: cannot access '/arquivo_inexistente': No such file or directory
Código de saída: 2

Note que o ls bem-sucedido retorna 0, enquanto o que falha retorna 2 (código comum para erro de arquivo não encontrado).

exit

O comando exit encerra o script atual e retorna um código de saída para o shell pai. Você pode usar exit com um número específico ou sem argumentos (que retorna o código do último comando). É uma boa prática usar exit 0 para sucesso e exit 1 (ou outros) para falhas.

Exemplo:

#!/bin/bash
if [ ! -f "$1" ]; then
    echo "Erro: arquivo $1 não encontrado."
    exit 1
fi
cat "$1"
exit 0

Se o arquivo passado como argumento não existir, o script imprime uma mensagem e termina com código 1. Caso contrário, exibe o conteúdo e sai com 0.

Convenções (0 = sucesso)

A convenção universal em sistemas Unix/Linux é: código 0 = sucesso, qualquer outro = falha. Não use 0 para indicar erro, pois isso quebra a compatibilidade com ferramentas padrão. Códigos comuns incluem:

  • 0: sucesso
  • 1: erro genérico
  • 2: uso incorreto (ex.: sintaxe inválida)
  • 126: comando encontrado mas não executável
  • 127: comando não encontrado
  • 130: script interrompido por Ctrl+C (sinal 2)
  • 255: código inválido (fora do range 0-255)

Respeitar essas convenções permite que outros scripts e ferramentas (como make, CI/CD) interpretem corretamente seus resultados.

Encadeando por status

O Bash oferece operadores lógicos para encadear comandos baseados no código de saída:

  • comando1 && comando2: executa comando2 apenas se comando1 retornar 0 (sucesso).
  • comando1 || comando2: executa comando2 apenas se comando1 retornar diferente de 0 (falha).
  • comando1 ; comando2: executa comando2 independentemente do resultado (sequencial).

Exemplo:

#!/bin/bash
# Cria diretório e, se bem-sucedido, copia arquivo
mkdir -p /tmp/meu_dir && cp /etc/hosts /tmp/meu_dir/ || echo "Falha na operação"

Se o mkdir falhar (ex.: permissão negada), o cp não será executado e a mensagem de erro será exibida.

Você também pode usar if para verificar explicitamente:

if comando; then
    echo "Sucesso"
else
    echo "Falha com código $?"
fi

Boas práticas

  • Sempre defina códigos de saída explícitos com exit em funções e scripts.
  • Documente os códigos de saída usados (ex.: 1 para erro de argumento, 2 para erro de arquivo).
  • Use set -e para interromper o script ao primeiro erro, mas com cuidado.
  • Evite códigos de saída ambíguos; prefira mensagens de erro claras no stderr.

Referências

Exercícios

  1. Crie um script que verifique se o usuário atual é 'root'. Se for, exiba "Root detectado" e saia com código 0; caso contrário, exiba "Não é root" e saia com código 1.

    ✓ Resposta:
    #!/bin/bash
    if [ "$(id -u)" -eq 0 ]; then
        echo "Root detectado"
        exit 0
    else
        echo "Não é root"
        exit 1
    fi
  2. Escreva um comando (não script) que tente criar um diretório e, se bem-sucedido, navegue para ele; caso contrário, exiba "Falha". Use operadores && e ||.

    ✓ Resposta:
    mkdir /tmp/teste && cd /tmp/teste || echo "Falha"
  3. O que será impresso pelo seguinte script? Explique.

    #!/bin/bash
    true
    echo $?
    false
    echo $?

    ✓ Resposta:O script imprime 0 e 1. O comando true sempre retorna 0 (sucesso), então $? é 0. O comando false sempre retorna 1 (falha), então $? é 1.
  4. Escreva um script que aceite um nome de arquivo como argumento. Se o arquivo existir e for legível, exiba seu conteúdo e saia com 0. Caso contrário, exiba uma mensagem de erro apropriada e saia com um código diferente de zero (use 1 para não existente, 2 para sem permissão).

    ✓ Resposta:
    #!/bin/bash
    if [ $# -eq 0 ]; then
        echo "Uso: $0 arquivo" >&2
        exit 2
    fi
    if [ ! -e "$1" ]; then
        echo "Erro: arquivo $1 não existe." >&2
        exit 1
    fi
    if [ ! -r "$1" ]; then
        echo "Erro: sem permissão de leitura para $1." >&2
        exit 2
    fi
    cat "$1"
    exit 0
  5. Explique a diferença entre os seguintes comandos e o que cada um faz com base no código de saída:

    comando1 && comando2 || comando3

    ✓ Resposta:O comando executa comando1. Se comando1 retornar 0 (sucesso), então comando2 é executado. Se comando2 também retornar 0, o || não executa comando3. Se comando1 falhar (retorno != 0), && impede comando2 e o || executa comando3. Se comando1 for bem-sucedido mas comando2 falhar, o || também executará comando3 (porque o resultado geral do && foi falha).