Linting Bash com ShellCheck: prevenindo erros comuns
1. Introdução ao ShellCheck e por que ele é essencial
ShellCheck é uma ferramenta de análise estática (linter) para scripts shell, que identifica erros de sintaxe, más práticas e potenciais vulnerabilidades antes mesmo da execução do script. Diferente de depuração em tempo de execução, o ShellCheck examina o código-fonte e emite avisos com códigos específicos (ex: SC2086) que apontam exatamente onde o problema está e como corrigi-lo.
Os principais benefícios incluem:
- Captura de erros silenciosos (como variáveis não entre aspas)
- Sugestões de boas práticas (uso de
[[ ]]em vez de[ ]) - Prevenção de vulnerabilidades de injeção de comandos
- Integração direta com editores (VS Code, Vim, Emacs) e pipelines CI/CD
Ao integrar o ShellCheck no fluxo de desenvolvimento, você reduz drasticamente o tempo gasto com depuração e evita bugs que só apareceriam em produção.
2. Instalação e configuração básica
A instalação é simples com gerenciadores de pacotes:
# Debian/Ubuntu
sudo apt install shellcheck
# macOS
brew install shellcheck
# Fedora
sudo dnf install ShellCheck
Para uma execução rápida:
shellcheck meu_script.sh
A saída exibe o número da linha, o código do aviso (ex: SC2086) e uma explicação. Para configurar regras globalmente, crie um arquivo .shellcheckrc no diretório do projeto ou home:
# .shellcheckrc
disable=SC2154,SC2086
enable=require-variable-braces
Isso desabilita avisos específicos e ativa verificações adicionais.
3. Erros comuns de sintaxe e substituições
Um dos erros mais frequentes é esquecer de colocar variáveis entre aspas duplas, o que causa quebra de palavras e expansão de glob:
# Ruim (SC2086)
arquivo="meu arquivo.txt"
cat $arquivo
# Bom
cat "$arquivo"
Outro erro comum é usar crases para substituição de comando:
# Ruim (SC2006)
data=`date`
# Bom
data=$(date)
Expressões aritméticas com $[] estão obsoletas:
# Ruim (SC2007)
soma=$[1 + 2]
# Bom
soma=$((1 + 2))
4. Problemas com condicionais e testes
O uso de [ ] vs [[ ]] tem diferenças cruciais:
# Ruim - quebra com strings vazias (SC2089)
if [ $variavel = "teste" ]; then
# Bom - seguro com strings vazias
if [[ $variavel == "teste" ]]; then
Para comparações numéricas, use operadores específicos:
# Ruim (SC2071)
if [ $idade == 18 ]; then
# Bom
if [ "$idade" -eq 18 ]; then
Espaçamento inadequado também é fonte de erros:
# Ruim - falta espaço após [
if [$nome = "João"]; then
# Bom
if [ "$nome" = "João" ]; then
5. Variáveis, escopo e boas práticas
Variáveis não declaradas podem causar comportamentos imprevisíveis:
# Ruim (SC2154) - $nome não foi declarada
echo "$nome"
# Bom - usar set -u para detectar
set -u
nome="Maria"
echo "$nome"
Problemas com export e escopo em funções:
# Ruim (SC2155) - export não funciona como esperado
export MINHA_VAR=$(comando_perigoso)
# Bom
MINHA_VAR=$(comando_seguro)
export MINHA_VAR
Use variáveis locais em funções para evitar poluição global:
minha_funcao() {
local temp="valor temporário"
echo "$temp"
}
6. Segurança e injeção de comandos
O uso de eval e substituições maliciosas é um risco real:
# Ruim (SC2046) - expansão de saída sem aspas
rm -rf $(find /tmp -type f)
# Bom
find /tmp -type f -delete
Redirecionamentos inseguros com find -exec:
# Ruim (SC2069) - redirecionamento por arquivo
find . -name "*.txt" -exec cat {} > saida.txt \;
# Bom
find . -name "*.txt" -exec cat {} \; > saida.txt
Armadilhas com read e separadores:
# Ruim (SC2162) - IFS padrão quebra linhas
while read linha; do
echo "$linha"
done < arquivo.txt
# Bom
while IFS= read -r linha; do
echo "$linha"
done < arquivo.txt
7. Integração com ferramentas de teste e CI
O ShellCheck pode ser usado com frameworks de teste como Bats:
#!/usr/bin/env bats
setup() {
shellcheck meu_script.sh
}
@test "Script passa no lint" {
run shellcheck meu_script.sh
[ "$status" -eq 0 ]
}
Em pipelines CI, configure para falhar se houver avisos críticos:
# GitHub Actions
- name: ShellCheck
run: shellcheck --severity=error *.sh
Para gerar relatórios formatados (JSON):
shellcheck -f json meu_script.sh > relatorio.json
Isso permite análise automatizada em ferramentas como SonarQube.
8. Dicas avançadas e personalização
Para desabilitar avisos específicos em trechos de código:
# shellcheck disable=SC2086
echo $variavel
Use o ShellCheck recursivamente em múltiplos scripts:
find . -name "*.sh" -exec shellcheck {} \;
Combine com outros linters para formatação consistente:
# bashate para estilo, shfmt para formatação
bashate -i E003 meu_script.sh
shfmt -w meu_script.sh
shellcheck meu_script.sh
Personalize o .shellcheckrc para seu projeto:
# .shellcheckrc
disable=SC1091,SC2001
enable=add-default-case
source-path=SCRIPTDIR
external-sources=true
Isso permite ignorar avisos de arquivos source externos e habilitar regras adicionais.
O ShellCheck é uma ferramenta indispensável para qualquer desenvolvedor que trabalhe com scripts shell. Ele não apenas previne erros comuns, mas também educa sobre boas práticas e segurança. Integrá-lo ao seu fluxo de desenvolvimento — seja no editor, no CI ou em testes automatizados — é um investimento que se paga rapidamente com menos bugs e scripts mais robustos.
Referências
- ShellCheck - Documentação Oficial — Site oficial com documentação completa, wiki e lista de todos os avisos (códigos SC).
- ShellCheck no GitHub — Repositório oficial com código-fonte, issues e exemplos de uso.
- Guia de Boas Práticas Shell do Google — Guia de estilo do Google para scripts shell, com muitas recomendações alinhadas ao ShellCheck.
- ShellCheck Integration with VS Code — Extensão oficial do ShellCheck para VS Code, com linting em tempo real.
- Bash Pitfalls - Greg's Wiki — Lista clássica de armadilhas comuns em Bash, muitas das quais são detectadas pelo ShellCheck.
- ShellCheck em Pipelines CI/CD — Wiki oficial com exemplos de integração em GitHub Actions, GitLab CI, Jenkins e outras ferramentas.