Opções do set para scripts robustos
Nesta aula, você aprenderá a usar as opções do comando set para tornar seus scripts Bash mais robustos e seguros. Vamos cobrir set -e, set -u, set -o pipefail e set -x, com exemplos práticos de como cada uma evita erros comuns e facilita o debug.
Escrever scripts Bash robustos é essencial para automações confiáveis. O comando set do Bash permite modificar o comportamento do shell em tempo de execução, ativando opções que previnem falhas silenciosas e facilitam a depuração. Nesta aula, exploraremos as quatro opções mais importantes: set -e, set -u, set -o pipefail e set -x. Ao final, você será capaz de escrever scripts que param ao encontrar erros, alertam sobre variáveis não definidas, tratam corretamente falhas em pipes e exibem cada comando executado.
set -e
A opção set -e (ou set -o errexit) faz com que o script termine imediatamente quando um comando retorna um código de saída diferente de zero (indicando erro). Por padrão, o Bash continua executando mesmo após um comando falhar, o que pode levar a comportamentos imprevisíveis e difíceis de depurar. Com set -e, qualquer erro interrompe o script, tornando-o mais previsível.
É importante notar que set -e não se aplica a comandos em condicionais (if, while, until) ou a comandos precedidos por ! (negação). Além disso, comandos em pipelines têm tratamento especial: apenas o último comando do pipe é verificado (a menos que set -o pipefail também esteja ativo).
Exemplo:
#!/bin/bash
set -e
echo "Este comando executa"
ls /diretorio/inexistente
echo "Este comando nunca será executado"
Neste exemplo, o script imprime a primeira mensagem, tenta listar um diretório que não existe (falha) e, como set -e está ativo, termina antes de imprimir a segunda mensagem.
set -u
A opção set -u (ou set -o nounset) trata o uso de variáveis não definidas como um erro, fazendo o script terminar. Por padrão, o Bash substitui variáveis não definidas por uma string vazia, o que pode mascarar erros de digitação ou lógica. Ativar set -u ajuda a detectar esses problemas precocemente.
É comum combinar set -u com set -e para maior robustez. No entanto, cuidado: se você tentar acessar uma variável que pode não estar definida em um contexto específico, o script falhará. Nesse caso, use expansão condicional como ${VAR:-default} para fornecer um valor padrão.
Exemplo:
#!/bin/bash
set -u
echo "Nome: $NOME" # NOME não está definida, script termina aqui
echo "Isso não será impresso"
Ao executar, o Bash exibirá uma mensagem de erro como "NOME: unbound variable" e sairá.
set -o pipefail
Em um pipeline (comando1 | comando2 | comando3), o código de saída do pipeline é o do último comando. Se um comando intermediário falhar, o pipeline ainda pode ser considerado bem-sucedido se o último comando tiver sucesso. A opção set -o pipefail altera esse comportamento: o código de saída do pipeline será o do primeiro comando que falhar (ou zero se todos tiverem sucesso). Isso é crucial para detectar falhas em pipelines.
Normalmente, set -o pipefail é usado em conjunto com set -e para garantir que qualquer falha em qualquer etapa do pipeline interrompa o script.
Exemplo:
#!/bin/bash
set -e
set -o pipefail
true | false | true # false falha, então o pipeline falha
echo "Isso não será executado"
Sem pipefail, o pipeline acima teria sucesso (porque o último comando é true). Com pipefail, o pipeline falha devido ao false no meio.
set -x (debug)
A opção set -x (ou set -o xtrace) ativa um modo de depuração que exibe cada comando antes de executá-lo, com os argumentos expandidos. A saída é precedida por um sinal de mais (+) e pode ser útil para entender o fluxo do script e identificar onde algo dá errado.
Você pode ativar set -x no início do script ou em partes específicas usando set -x para ligar e set +x para desligar. Isso evita que a saída fique poluída com informações de debug em todo o script.
Exemplo:
#!/bin/bash
set -x # Ativa debug
nome="João"
echo "Olá, $nome"
set +x # Desativa debug
echo "Isso não será mostrado com +"
Ao executar, você verá algo como:
+ nome=João
+ echo 'Olá, João'
Olá, João
+ set +x
Isso não será mostrado com +
Boas práticas e observações finais
Recomenda-se iniciar scripts com set -euo pipefail para ativar as três opções de robustez. Isso garante que erros sejam detectados e o script pare, variáveis não definidas causem falha e pipelines reportem corretamente falhas intermediárias. O debug com set -x pode ser ativado condicionalmente, por exemplo, com uma variável de ambiente DEBUG.
Lembre-se de que set -e pode ser surpreendente em alguns casos, como comandos que retornam código diferente de zero em situações esperadas (por exemplo, grep que não encontra padrão). Nesses casos, use || true ou condicionais explicitamente.
Referências
- GNU Bash Manual: The Set Builtin
- Advanced Bash-Scripting Guide: Command-Line Options
- BashFAQ/105: Why doesn't set -e work inside a function?
- ShellCheck - A shell script static analysis tool
- Google Shell Style Guide
Exercícios
- Crie um script que ative
set -ee tente executar um comando que falha (ex:ls /naoexiste). Observe que o script termina antes de um comando posterior. - Escreva um script que use
set -ue tente imprimir uma variável não definida. O que acontece? - Crie um script com
set -eeset -o pipefailque contenha um pipeline onde o comando do meio falhe. Mostre que o script termina. - Use
set -xpara depurar um script simples que define uma variável e imprime uma mensagem. Execute e veja a saída. - Combine todas as opções (
set -euo pipefail) em um script que tente ler um arquivo inexistente e depois imprima algo. Explique o comportamento.
#!/bin/bash
set -e
echo "Antes do erro"
ls /naoexiste
echo "Depois do erro" # Não executa
#!/bin/bash
set -u
echo "Valor: $NAODEFINIDA" # Erro: unbound variable
#!/bin/bash
set -e
set -o pipefail
true | false | true
echo "Isso não será executado"
#!/bin/bash
set -x
nome="Maria"
echo "Olá, $nome"
set +x
echo "Fim"
#!/bin/bash
set -euo pipefail
cat /arquivo/inexistente | wc -l
echo "Isso não será executado"