Bash Scripting — Boas Práticas para o Dia-a-Dia
Guia completo de boas práticas em Bash scripting: error handling com set -euo pipefail, debugging, traps, subshells, funções portáteis e dicas para escrever scripts robustos e manuteníveis.
O Bash é simples, omnipresente e perigoso se não souberes o que estás a fazer. Já perdi horas a debuggar scripts que correram em produção com variáveis vazias. Depois de set -euo pipefail, nunca mais.
Durante anos, os meus scripts Bash eram um monstro — funcionavam no meu Mac, partiam no servidor, e quando algo corria mal, o script continuava feliz como se nada tivesse acontecido. Até que um dia um rm -rf com uma variável vazia quase me custou um servidor inteiro.
Foi aí que percebi que escrever Bash não é só colar comandos. É uma arte de disciplina: error handling, quoting consistente, funções bem definidas, e ferramentas como o shellcheck que te salvam de ti próprio.
A diferença entre um script que funciona e um que não parte em produção resume-se a meia dúzia de práticas. Error handling com set -euo pipefail, debugging com set -x, funções bem desenhadas e quoting consistente. E um linter — o shellcheck — que te salva de ti próprio antes de fazeres deploy. São estas as práticas que mais me custaram a aprender e que hoje considero obrigatórias.
Script Bash a correr com debugging ativo (set -x) e cores de output
Error Handling: o teu salva-vidas
Por omissão, o Bash não para quando um comando falha. Isto é aterrador. Se fizeres cd /diretorio-que-nao-existe && rm -rf ., o Bash queixa-se do cd mas faz o rm na mesma no diretório atual. Já viste o perigo?
Cada letra tem um propósito:
| Flag | O que faz | Exemplo |
|---|---|---|
| -e | Sai logo se um comando falhar (exit code ≠ 0) | cd /x || exit 1 |
| -u | Erro ao usar variável não definida | echo $NAO_EXISTO → erro |
| -o pipefail | Se um comando no pipe falhar, o pipe todo falha | grep foo /x | sort → falha se grep falhar |
Mas há cenários onde queres ignorar erros específicos. Usa || true para isso:
trap — o paraquedas de emergência
O trap permite-te executar código quando o script recebe sinais ou termina. Uso essencial: limpar ficheiros temporários:
Dica: Usa trap 'comando' EXIT para garantires que a limpeza corre sempre, mesmo que o script rebente a meio. O trap ERR é utilíssimo para logging de erros.
Debugging: set -x e amigos
Quando um script não faz o que queres, o set -x é o teu melhor amigo. Ele mostra cada comando antes de executar, com expansão de variáveis:
Outras técnicas de debugging:
- bash -n script.sh — verifica sintaxe sem executar (linter básico)
- export SHELLOPTS — passa opções para sub-shells
- PS4='+ $BASH_SOURCE:$LINENO ' — personaliza o output do set -x com ficheiro e linha
Subshells: $() vs ( ) vs { }
Esta é uma das maiores fontes de confusão no Bash. Vamos clarificar:
| Sintaxe | Tipo | Comportamento |
|---|---|---|
| $(comando) | Substituição de comando | Corre o comando e devolve o output. Não cria subshell pai |
| (comandos) | Subshell | Corre comandos num shell filho. Variáveis não afetam o pai |
| { comandos; } | Grouping | Corre no mesmo shell. Mais eficiente. Espaços obrigatórios |
Exemplo prático para perceberes a diferença:
Atenção: Usa { } em vez de ( ) sempre que não precisares de isolamento. É mais rápido e consome menos memória. Em loops com milhares de iterações, a diferença é notória.
Funções: local, return e argumentos
Funções são o que separa um script organizado de uma sopa de comandos.
Regras de ouro para funções:
- Sempre local nas variáveis — não poluas o scope global
- return apenas para código de erro (0-255). Para devolver dados, usa echo ou variáveis globais
- Argumentos: $1, $2, ... $@ para todos, $# para contagem
- Valida sempre os argumentos no início da função
Boas práticas essenciais
Ao longo dos anos, cristalizei um conjunto de regras que todo o script Bash devia seguir:
Quoting — sempre que há espaços
[[ ]] vs [ ] — a batalha dos tests
| [ ] (POSIX) | [[ ]] (Bash) |
|---|---|
| Disponível em qualquer shell POSIX | Apenas Bash/Zsh/Ksh |
| Não suporta &&, || dentro | Suporta &&, ||, ! naturalmente |
| Precisa de quoting para variáveis vazias | Lida com strings vazias sem problemas |
| Sem suporte para regex | =~ para regex nativo |
Variáveis com valores por omissão
Variáveis readonly e constantes
Ferramentas que te salvam a vida
| Ferramenta | O que faz |
|---|---|
| shellcheck | Linter para Bash — deteta quoting errors, variáveis não usadas, problemas de sintaxe. Instala com brew install shellcheck ou apt install shellcheck |
| bash -n | Verifica sintaxe sem executar — rápido para CI/CD |
| shfmt | Formatador automático de Bash — indenta, alinha, normaliza |
Output do shellcheck a identificar problemas num script — repara nos números de linha e sugestões
.bashrc vs .bash_profile vs .profile
A confusão entre estes ficheiros é lendária. Vamos simplificar:
| Ficheiro | Quando é lido |
|---|---|
| ~/.bash_profile | Login shells (ssh, terminal ao abrir) |
| ~/.bashrc | Shells interativos não-login (terminal dentro de terminal) |
| ~/.profile | Fallback se ~/.bash_profile não existir |
A dica de ouro: source um pelo outro para não teres duplicação:
Exemplo completo: script de backup com boas práticas
O shebang correto
Usa sempre #!/usr/bin/env bash em vez de #!/bin/bash. Porquê?
- env procura o Bash no $PATH do utilizador
- Funciona em macOS (onde /bin/bash é versão antiga) e em NixOS (onde o Bash não está em /bin)
- Mais portável entre sistemas (o /usr/bin/env é praticamente garantido; o caminho do Bash não)
Dica final: O teu script devia começar sempre com o bloco: #!/usr/bin/env bash, set -euo pipefail, e a declaração de readonly constants. O shellcheck devia passar sem warnings. E cada função devia caber no ecrã sem scroll. Segue estas regras e os teus scripts vão durar anos.
Recapitulando
- set -euo pipefail em todo o lado — error handling não é opcional
- trap para limpeza e logging de erros
- $() para substituição, () para subshells, {} para grouping
- Funções com local, validação de argumentos e return consistente
- Quoting SEMPRE nas variáveis, [[ ]] em vez de [ ]
- ${VAR:-default} para valores por omissão
- shellcheck antes de cada deploy
O Bash é uma linguagem traiçoeira. Mas com disciplina, as ferramentas certas e estas boas práticas, os teus scripts vão ser robustos, legíveis e portáveis.
Experimenta executar o shellcheck num dos teus scripts antigos. Aposto que encontras pelo menos 5 coisas para corrigir.
Recursos adicionais
Partilhar
Comentários 0
Ainda sem comentários — sê o primeiro.