Escrever scripts bash que sejam fáceis de manter, entender e reutilizar é uma habilidade essencial para qualquer profissional de TI. Nesta aula, você aprenderá a estruturar scripts de forma profissional, aplicando técnicas como cabeçalho documentado, funções bem definidas, função main() e uso de constantes. Essas práticas transformam scripts simples em programas robustos, prontos para ambientes de produção.

Vamos explorar cada elemento com exemplos práticos, mostrando como a organização do código impacta na legibilidade, depuração e escalabilidade. Ao final, você terá um modelo de script profissional que pode servir de base para seus projetos.

Cabeçalho e documentação

O cabeçalho de um script bash é a primeira impressão que outros desenvolvedores terão do seu código. Um cabeçalho bem elaborado deve conter informações essenciais sobre o script: nome, descrição, autor, data, versão, licença e como usá-lo. Isso não é apenas uma formalidade; documentação clara reduz o tempo de onboarding e evita erros de uso.

Além do cabeçalho, é importante documentar o código ao longo do script, explicando a lógica de partes complexas. Comentários devem ser objetivos e úteis, não apenas repetir o que o código faz. Uma boa prática é usar comentários para explicar o porquê, não o como.

Exemplo de cabeçalho profissional:

#!/usr/bin/env bash
#
# Script: backup.sh
# Descrição: Realiza backup de diretórios para um local remoto.
# Autor: João Silva
# Data: 2025-01-15
# Versão: 1.0.0
# Licença: MIT
# Uso: ./backup.sh [diretório_origem] [destino]
# Dependências: rsync, tar
#
# Este script executa um backup incremental usando rsync.
# O diretório de origem é compactado e enviado ao destino.
#
# Exemplos:
#   ./backup.sh /home/user/docs /mnt/backup
#   ./backup.sh --help
#

Note o uso de #!/usr/bin/env bash para portabilidade, e o bloco de comentários com informações estruturadas. Também é útil incluir a data de última modificação e um histórico de versões.

Funções

Funções são blocos de código reutilizáveis que executam tarefas específicas. Em bash, funções são definidas com a sintaxe nome() { ... } ou function nome { ... }. Elas ajudam a evitar repetição, tornam o script mais modular e facilitam testes e manutenção.

Ao criar funções, siga boas práticas: use nomes descritivos (verbo + substantivo), mantenha funções curtas e com um propósito único, e documente cada função com um comentário explicando o que faz, parâmetros e retorno.

Exemplo de funções bem estruturadas:

#!/usr/bin/env bash

# Função para exibir ajuda
mostrar_ajuda() {
    cat <<EOF
Uso: $(basename "$0") [opções]

Opções:
  -h, --help  Exibe esta ajuda
  -v, --version  Exibe a versão
EOF
}

# Função para verificar dependências
verificar_dependencias() {
    local deps=("rsync" "tar")
    for dep in "${deps[@]}"; do
        if ! command -v "$dep" >/dev/null 2>&1; then
            echo "Erro: dependência '$dep' não encontrada." >&2
            exit 1
        fi
    done
}

# Função principal de backup
realizar_backup() {
    local origem="$1"
    local destino="$2"
    echo "Iniciando backup de $origem para $destino..."
    # ... lógica de backup ...
}

Note que usamos local para declarar variáveis dentro das funções, evitando poluir o escopo global. Também usamos command -v para verificar a existência de comandos.

main()

A função main() é uma convenção em muitas linguagens de programação para indicar o ponto de entrada do programa. Em bash, podemos criar uma função main() que organiza a execução principal do script, tornando o fluxo mais claro e facilitando a chamada de funções em ordem controlada.

Ter uma função main() também permite que o script seja executado como um módulo, pois podemos verificar se ele está sendo executado diretamente ou importado por outro script, usando ${BASH_SOURCE[0]} == "$0".

Estrutura típica:

#!/usr/bin/env bash

# Funções...

main() {
    verificar_dependencias
    # processar argumentos
    # chamar funções específicas
    realizar_backup "$@"
}

# Chamada da função principal apenas se o script for executado diretamente
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
    main "$@"
fi

O teste if no final garante que, se o script for incluído em outro (via source), a função main() não seja executada automaticamente, permitindo reutilização das funções.

Constantes

Constantes são variáveis cujos valores não devem ser alterados durante a execução do script. Em bash, usamos readonly para declarar constantes. Elas são úteis para valores fixos como caminhos, configurações, mensagens padrão, etc.

Usar constantes melhora a legibilidade e evita erros de digitação, além de centralizar configurações em um único lugar. Convenção comum é usar nomes em MAIÚSCULAS com underscores.

Exemplo:

#!/usr/bin/env bash

# Constantes
readonly SCRIPT_NOME="$(basename "$0")"
readonly DIR_ORIGEM_PADRAO="/home/user/docs"
readonly DIR_DESTINO_PADRAO="/mnt/backup"
readonly LOG_FILE="/var/log/backup.log"
readonly VERSION="1.0.0"

# Uso das constantes
main() {
    echo "Iniciando $SCRIPT_NOME versão $VERSION"
    local origem="${1:-$DIR_ORIGEM_PADRAO}"
    local destino="${2:-$DIR_DESTINO_PADRAO}"
    # ...
}

Note o uso de ${1:-$DIR_ORIGEM_PADRAO} para usar o valor padrão caso o argumento não seja passado. Isso torna o script mais flexível.

Boas práticas e observações finais

Além dos tópicos abordados, algumas boas práticas ajudam a tornar seus scripts mais profissionais:

  • Use set -euo pipefail no início para sair em erros, evitar variáveis não definidas e capturar falhas em pipelines.
  • Trate os argumentos com getopts ou bibliotecas como argparse (em bash, getopts é nativo).
  • Divida o script em arquivos menores se ele ficar muito grande, usando source para incluir bibliotecas.
  • Teste seu script com shellcheck para identificar problemas de sintaxe e boas práticas.
  • Adicione tratamento de erros com trap para limpar recursos em caso de interrupção.

Lembre-se: um script profissional é aquele que pode ser mantido por outras pessoas sem dificuldade. Invista tempo na estrutura e documentação.

Exercícios

  1. Crie um script bash que declare uma constante NOME_EMPRESA com valor "TechCorp" e uma função saudacao() que imprima "Bem-vindo à $NOME_EMPRESA". Execute a função.

    ✓ Resposta:
    #!/usr/bin/env bash
    
    readonly NOME_EMPRESA="TechCorp"
    
    saudacao() {
        echo "Bem-vindo à $NOME_EMPRESA"
    }
    
    saudacao
    
  2. Escreva um script com uma função main() que exiba a versão do script (constante VERSAO = "1.0") e depois chame uma função processar() que imprime "Processando...". O script deve executar main apenas se for executado diretamente.

    ✓ Resposta:
    #!/usr/bin/env bash
    
    readonly VERSAO="1.0"
    
    processar() {
        echo "Processando..."
    }
    
    main() {
        echo "Versão $VERSAO"
        processar
    }
    
    if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
        main "$@"
    fi
    
  3. Adicione um cabeçalho completo ao script do exercício 2, incluindo descrição, autor, data e licença.

    ✓ Resposta:
    #!/usr/bin/env bash
    #
    # Script: exemplo.sh
    # Descrição: Script de exemplo com main() e constantes.
    # Autor: Seu Nome
    # Data: 2025-01-01
    # Versão: 1.0
    # Licença: MIT
    # Uso: ./exemplo.sh
    #
    
    readonly VERSAO="1.0"
    
    processar() {
        echo "Processando..."
    }
    
    main() {
        echo "Versão $VERSAO"
        processar
    }
    
    if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
        main "$@"
    fi
    
  4. Crie um script que defina uma constante DIR_DESTINO com valor "/tmp/backup" e uma função criar_diretorio() que verifica se o diretório existe e o cria se não existir. Use mkdir -p.

    ✓ Resposta:
    #!/usr/bin/env bash
    
    readonly DIR_DESTINO="/tmp/backup"
    
    criar_diretorio() {
        if [[ ! -d "$DIR_DESTINO" ]]; then
            mkdir -p "$DIR_DESTINO"
            echo "Diretório $DIR_DESTINO criado."
        else
            echo "Diretório $DIR_DESTINO já existe."
        fi
    }
    
    criar_diretorio
    
  5. Escreva um script que use set -euo pipefail e uma função soma() que receba dois números e imprima a soma. O script deve chamar soma com os números 3 e 5.

    ✓ Resposta:
    #!/usr/bin/env bash
    set -euo pipefail
    
    soma() {
        local a="$1"
        local b="$2"
        echo $((a + b))
    }
    
    soma 3 5
    

Referências