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