Bem-vindo à aula 71! Até agora, você aprendeu a escrever scripts Bash funcionais, mas escrever código que funciona é apenas o começo. Em projetos reais, especialmente quando os scripts crescem em complexidade, erros sutis podem passar despercebidos e causar falhas em produção. É aqui que entra a análise estática: uma técnica que examina o código sem executá-lo, procurando por padrões problemáticos, bugs potenciais e más práticas.

Nesta aula, vamos focar no ShellCheck, a ferramenta padrão de análise estática para scripts shell. Você vai entender o que ela detecta, quais são os avisos mais comuns, como integrá-la em seu ambiente de desenvolvimento e como usá-la para melhorar a qualidade dos seus scripts. Ao final, você terá um conjunto de boas práticas que elevará seus scripts a um nível profissional.

O que detecta

O ShellCheck é um analisador estático que examina scripts shell (principalmente Bash, mas também POSIX sh, Dash, Ksh, entre outros) e emite avisos e erros sobre problemas potenciais. Ele não executa o script, mas sim analisa a sintaxe e a semântica, procurando por armadilhas comuns que podem levar a comportamentos inesperados, falhas de segurança ou simplesmente código confuso.

Os problemas detectados podem ser classificados em várias categorias:

  • Erros de sintaxe: comandos mal formados, parênteses desbalanceados, aspas não fechadas, etc.
  • Problemas de portabilidade: uso de recursos que não estão disponíveis em todas as shells (ex.: [[ ]] não é POSIX).
  • Armadilhas de expansão: uso incorreto de variáveis, falta de aspas em expansões que podem quebrar.
  • Problemas de segurança: uso inseguro de eval, caminhos não sanitizados, etc.
  • Más práticas: uso de comandos obsoletos, falta de tratamento de erros, etc.

Por exemplo, considere o script abaixo:

#!/bin/bash
# script com problemas
nome=$1
if [ $nome == "joao" ]
then
    echo "Olá, João"
fi

O ShellCheck detectaria vários problemas: a variável $nome não está entre aspas (o que pode causar erros se contiver espaços), o uso de == dentro de [ ] não é POSIX (deveria ser =), e a variável $1 pode não estar definida. Vamos ver isso em ação mais adiante.

O ShellCheck é extremamente útil para encontrar bugs antes que eles aconteçam. Ele é usado em muitos projetos open source e é uma ferramenta padrão em pipelines de CI (Integração Contínua) para garantir a qualidade dos scripts.

Avisos comuns

O ShellCheck emite avisos com códigos numéricos, que variam de 1000 a 2999. Cada código indica um tipo específico de problema. Vamos explorar os avisos mais comuns que você encontrará:

  • SC2086: "Double quote to prevent globbing and word splitting." — Este é um dos avisos mais frequentes. Ocorre quando você usa uma variável sem aspas, como echo $var. O ShellCheck recomenda usar "$var" para evitar que o valor seja dividido em palavras ou interpretado como padrão de globbing.
  • SC2164: "Use 'cd ... || exit' or 'cd ... || return' in case cd fails." — Quando você usa cd em um script, se o diretório não existir, o script continua executando em um diretório errado. O ShellCheck sugere tratar o erro imediatamente.
  • SC2002: "Useless cat. Consider 'cmd < file' or 'cmd file' instead." — O uso de cat arquivo | comando é desnecessário; você pode redirecionar diretamente.
  • SC2034: "Var appears unused. Verify it or export it." — Uma variável foi definida, mas nunca usada. Isso pode indicar um erro de digitação ou código morto.
  • SC2016: "Expressions don't expand in single quotes, use double quotes for that." — Ocorre quando você usa aspas simples em uma string que contém $ e espera expansão.

Vamos ver um exemplo prático que gera vários avisos:

#!/bin/bash
# exemplo com problemas
for arquivo in $(ls *.txt)
do
    echo "Processando $arquivo"
    cat $arquivo | grep "erro"
done

Ao rodar o ShellCheck, você obteria:

$ shellcheck exemplo.sh

In exemplo.sh line 2:
for arquivo in $(ls *.txt)
              ^-- SC2045: Iterating over ls output is fragile. Use globs.

In exemplo.sh line 4:
echo "Processando $arquivo"
                   ^-- SC2086: Double quote to prevent globbing and word splitting.

In exemplo.sh line 5:
cat $arquivo | grep "erro"
    ^-- SC2086: Double quote to prevent globbing and word splitting.
    ^-- SC2002: Useless cat. Consider 'cmd < file' or 'cmd file' instead.

O ShellCheck não apenas aponta o problema, mas também sugere a correção. Você pode usar a opção -f para formatar a saída de várias maneiras, como JSON, TAP, etc.

Integração

O ShellCheck pode ser integrado de várias maneiras no seu fluxo de trabalho. Aqui estão as principais:

Linha de comando

O uso mais básico é executar o ShellCheck em um arquivo:

shellcheck meu_script.sh

Para ver a saída em formato JSON (útil para ferramentas):

shellcheck -f json meu_script.sh

Você também pode especificar o shell alvo com -s (ex.: -s dash).

Editores de texto e IDEs

O ShellCheck é integrado a muitos editores populares:

  • VS Code: instale a extensão "ShellCheck" da Microsoft.
  • Vim: use o plugin ale ou syntastic.
  • Emacs: use flycheck com o suporte a ShellCheck.
  • Sublime Text: instale o pacote "SublimeLinter-shellcheck".

Essas integrações exibem avisos em tempo real enquanto você digita, o que ajuda a corrigir problemas imediatamente.

CI/CD

Em pipelines de integração contínua, você pode executar o ShellCheck como uma etapa de verificação. Por exemplo, em um GitHub Action:

- name: Run ShellCheck
  uses: ludeeus/action-shellcheck@master
  with:
    scandir: './scripts'

Ou em um script Jenkins:

shellcheck scripts/*.sh

Isso garante que nenhum script com problemas seja mesclado no repositório.

Pré-commit

Você pode usar o pre-commit para rodar o ShellCheck antes de cada commit. Adicione ao seu arquivo .pre-commit-config.yaml:

repos:
  - repo: https://github.com/koalaman/shellcheck-precommit
    rev: v0.9.0
    hooks:
      - id: shellcheck

Boas práticas

Aqui estão algumas boas práticas para aproveitar ao máximo o ShellCheck e melhorar a qualidade dos seus scripts:

  • Sempre use aspas duplas em variáveis: a menos que você queira intencionalmente a divisão de palavras (o que é raro), use "$var".
  • Evite ls em loops: use globs (*.txt) em vez de for arquivo in $(ls *.txt).
  • Trate erros de cd: use cd diretorio || exit para evitar continuar no diretório errado.
  • Prefira [[ ]] em Bash: se você está escrevendo para Bash, use [[ ]] em vez de [ ], pois é mais seguro e poderoso.
  • Seja explícito com o shebang: declare #!/bin/bash ou #!/usr/bin/env bash para que o ShellCheck saiba qual shell usar.
  • Use set -e ou set -o pipefail com cuidado: o ShellCheck pode ajudar a identificar onde esses comandos podem causar problemas.
  • Revise os avisos, não apenas os ignore: cada aviso é uma oportunidade de aprender e melhorar.

Um exemplo de script corrigido com base nos avisos:

#!/bin/bash
# exemplo corrigido
for arquivo in *.txt
do
    echo "Processando $arquivo"
    grep "erro" "$arquivo" || echo "Nenhum erro encontrado"
done

Ao rodar o ShellCheck novamente, nenhum aviso será emitido.

Referências

Exercícios

  1. Instale o ShellCheck no seu sistema (se ainda não estiver instalado) e verifique a versão. Qual comando você usou?
  2. ✓ Resposta: sudo apt install shellcheck (no Debian/Ubuntu) ou brew install shellcheck (no macOS). Para verificar: shellcheck --version.
  3. O que o aviso SC2086 indica e como corrigi-lo? Dê um exemplo.
  4. ✓ Resposta: SC2086 indica que uma expansão de variável não está entre aspas, o que pode causar globbing e word splitting. Exemplo: echo $nomeecho "$nome".
  5. Crie um script Bash que contenha pelo menos três problemas que o ShellCheck detectaria (por exemplo, uso de ls em loop, variável sem aspas, cat desnecessário). Execute o ShellCheck e liste os avisos.
  6. ✓ Resposta: Exemplo:
    #!/bin/bash
    for arq in $(ls *.txt)
    do
        cat $arq | grep "x"
    done
    Avisos: SC2045 (ls em loop), SC2086 (variável sem aspas), SC2002 (cat desnecessário).
  7. Como você integraria o ShellCheck em um pipeline de CI usando GitHub Actions? Escreva o trecho do YAML.
  8. ✓ Resposta:
    - name: Run ShellCheck
      uses: ludeeus/action-shellcheck@master
      with:
        scandir: './scripts'
  9. Explique a diferença entre [ ] e [[ ]] no Bash e por que o ShellCheck recomenda [[ ]] para scripts Bash.
  10. ✓ Resposta: [ ] é o comando test, POSIX, que tem limitações (ex.: não suporta regex, precisa de aspas em variáveis). [[ ]] é uma palavra-chave do Bash que é mais segura e poderosa (suporta regex, operadores && e || sem aspas). O ShellCheck recomenda [[ ]] porque é mais robusto e evita erros comuns.

Considerações finais

O ShellCheck é uma ferramenta indispensável para qualquer desenvolvedor que trabalhe com shell scripting. Ele não apenas ajuda a evitar bugs, mas também ensina boas práticas através de seus avisos. Ao incorporá-lo no seu fluxo de trabalho, você escreverá scripts mais confiáveis, seguros e portáveis. Lembre-se de que a análise estática é uma camada de proteção, mas não substitui testes e revisões de código. Use o ShellCheck como parte de uma estratégia mais ampla de qualidade de software.