Como verificar se tudo funcionou mesmo após rodar um script ou comando no shell
Muita gente acha que ver se um comando foi executado é só olhar o terminal. Não é. Você precisa entender o que está acontecendo nos bastidores quando o processo termina e o interpretador mostra (ou não) uma mensagem de erro. Isso parece bobo, mas é onde a maior parte dos problemas silenciosos aparece.
Verificando ocorreu ou correu tudo bem
O shell retorna um código de saída inteiro para cada comando. Zero significa sucesso. Qualquer coisa diferente de zero é uma falha, e existem dezenas de tipos diferentes de falha dependendo do comando. O problema é que a maioria das pessoas nunca olha para esse número. Elas veem uma linha bonita no log e assumem que deu certo. A maneira mais direta de verificar se ocorreu ou correu tudo bem é usar a variável especial `$?` imediatamente após o comando que você quer testar. Isso te dá o código de saída no exato momento em que ele foi gerado. Se você fizer qualquer outra coisa no meio, como rodar um `echo` ou uma variável, o `$?` muda e vira lixo. A sequência precisa ser: comando alvo, depois `$?` sem espaço entre eles.
Vou dar um exemplo prático. Tem um dia que eu tava debugando um job de backup que ia rodar todo dia às 3h da manhã. O cron registrava saída zero, parecia tudo certo. Mas os arquivos não estavam sendo criados. Descobri que o script usava `cd /diretorio/de_backup && rsync ...` e o diretório tinha sido movido meses antes por uma manutenção. O `cd` falhava silenciosamente porque o redirecionamento de output do cron estava desligado. O `&&` parava a execução e retornava o código de erro do `cd`, mas como não havia log de erro ativo, eu via apenas sucesso no monitoring. A solução foi adicionar `set -e` no início do script e configurar um syslog dedicado pra aquela jobs. Isso custou uns 20 minutos de configuração e me salvou de perder dados por mais três semanas.
O código de saída não diz tudo
Um dos conceitos errados mais comuns é achar que código zero sempre significa sucesso real. Não significa. Alguns programas retornam zero mesmo quando algo ruim aconteceu internamente, especialmente se eles têm tratamento de erro próprio e decidem continuar rodando. Um dump de banco de dados pode retornar zero enquanto pula tabelas inteiras por falta de permissão. Um script Python pode terminar com código zero se você não configurou o exit code corretamente no bloco `finally`. O inverso também é verdade. Um comando pode falhar com código não-zero mas o efeito colateral já aconteceu. Um `cp` que falha pela metade deixa metade do arquivo copiado. Um deploy que falha no segundo servidor já atualizou o primeiro. Verificar o código de saída é necessário mas não suficiente. Você precisa validar o resultado real.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Métodos práticos de validação
Dependendo do que você tá rodando, existem abordagens diferentes. Vou listar as mais úteis no dia a dia. Validação direta com condicional: A forma mais comum é encadear comandos com `&&` para sucesso e `||` para falha. Exemplo: `./minha_tarefa.sh && echo "OK" || echo "FALHOU com código $?"`. Isso é simples e funciona na maioria dos casos. O limitador aqui é que ele só mostra o código de erro, não o que aconteceu. Se o script tiver saída longa no stderr, você pode perder informações importantes porque o `||` não captura o texto, só o código numérico.
Redirecionando stderr e stdout: Um passo acima é capturar ambas as saídas em um arquivo e verificar o conteúdo. `./meu_comando.sh > log.txt 2>&1; echo $? >> log_status.txt`. Assim você tem o rastro completo. O detalhe importante é a ordem dos redirecionadores. Se você inverter para `2>&1 > log.txt`, o stderr vai para o terminal e só o stdout vai pro arquivo. Essa armadilha pega todo mundo pelo menos uma vez. Usando ferramentas de teste: Se você tá rodando scripts Python, existe o módulo `subprocess` com verificação explícita. Em Bash avançado, o comando `command -v` e o operador `time` ajudam a entender se algo rodou dentro do esperado ou travou. Para pipelines inteiros, o Bash 4.2+ oferece `set -o pipefail`, que faz o pipeline inteiro falhar se qualquer etapa dentro dele falhar. Sem isso, o último comando do pipeline define o código de saída, o que pode mascarar falhas nas etapas anteriores.
Quando a verificação simples não funciona
Certamente existem cenários onde olhar o código de saída não resolve. Processos que rodão em background, containers que são reiniciados automaticamente por um orchestator, ou serviços que reagem de forma diferente dependendo do estado interno. Nesses casos, você precisa de monitoramento ativo, não só de verificação pós-execução. Se você trabalha com sistemas distribuídos ou micro-serviços, a verificação manual de cada comando perde o sentido. Aí o caminho é usar ferramentas como Prometheus com alertas, ou no mínimo um script de health-check que roda periodicamente e valida o estado real do sistema, não apenas o código de saída. Isso evita aquele cenário clássico onde você descobre que algo falhou três dias depois porque o código de saída era zero mas o arquivo de configuração tinha sido corrompido.
O básico funciona na grande maioria das vezes. Verificar `$?`, usar `pipefail`, e redirecionar saídas pra um log é suficiente pra 90% dos problemas do dia a dia. O resto exige construir uma camada de validação acima do que o shell oferece nativamente.