Dominando a Análise de Argumentos Bash para Scripts Poderosos

Analise argumentos Bash com parâmetros posicionais, getopts, loops de opções longas, valores padrão e erros de uso claros.

Dominando a Análise de Argumentos Bash para Scripts Poderosos

Os argumentos de linha de comando são o que tornam seus scripts Bash flexíveis. Quando a análise de argumentos Bash é fraca, você acaba editando variáveis dentro do script toda vez que precisa de um arquivo, host ou modo diferente.

Este guia mostra como lidar com argumentos posicionais, opções curtas com getopts e opções longas com um loop while/case. O objetivo é um script que rejeita entradas ruins cedo e informa exatamente como executá-lo.

O Básico: Argumentos Posicionais

Antes de lidar com flags e opções nomeadas, é crucial entender como o Bash lida com argumentos simples e sequenciais.

Variável Descrição Exemplo se chamado como ./script.sh foo bar
$0 O nome do script em si ./script.sh
$1, $2, ... O primeiro, segundo, etc., argumento posicional foo, bar
$# A contagem de argumentos posicionais 2
$@ Todos os argumentos posicionais, tratados como strings separadas foo bar
$* Todos os argumentos posicionais, tratados como uma única string foo bar

Exemplo: Verificação Posicional Simples

#!/bin/bash

if [ "$#" -ne 2 ]; then
    echo "Uso: $0 <arquivo_origem> <diretorio_destino>"
    exit 1
fi

ORIGEM="$1"
DESTINO="$2"

echo "Copiando $ORIGEM para $DESTINO..."
# cp "$ORIGEM" "$DESTINO"

Análise Padrão com getopts para Opções Curtas

Para scripts profissionais que exigem opções como -v (verboso) ou -f <arquivo>, o utilitário embutido getopts é o método canônico e mais confiável em Bash puro. Ele é projetado especificamente para opções curtas (um único caractere).

Como getopts Funciona

getopts lê opções sequencialmente, definindo a variável designada (geralmente OPT) para a opção encontrada e colocando o valor de qualquer argumento (se necessário) na variável OPTARG.

  • String de Opção (OPTSTRING): Define opções válidas. Se uma opção requer um argumento (por exemplo, -f nome_arquivo), siga o caractere com dois pontos (:). Exemplo: vho: permite -v, -h e -o que requer um valor.
  • OPTIND: Um índice interno do Bash que rastreia o próximo argumento a ser processado. Ele deve ser inicializado como 1 (que é o padrão, mas às vezes redefinido em scripts complexos).

Modelo Prático de getopts

Este modelo lida com três opções: -v (flag), -h (flag) e -f (opção que requer um valor).

#!/bin/bash

# --- Padrões ---
VERBOSO=0
NOME_ARQUIVO="padrao.txt"

# --- Função de Uso ---
uso() {
    echo "Uso: $0 [-v] [-h] [-f <arquivo>] <entrada>"
    exit 1
}

# --- Loop de Análise de Argumentos ---
while getopts ":vhf:" OPT; do
    case "$OPT" in
        v)
            VERBOSO=1
            echo "Modo verboso ativado."
            ;;
        h)
            uso
            ;;
        f)
            # OPTARG contém o argumento fornecido para -f
            NOME_ARQUIVO="$OPTARG"
            echo "Nome do arquivo de saída definido como: $NOME_ARQUIVO"
            ;;
        \?)
            # Lida com opções não reconhecidas
            echo "Erro: Opção inválida -$OPTARG" >&2
            uso
            ;;
        :)
            # Lida com argumentos ausentes para opções que requerem um (por exemplo, -f sem um nome de arquivo)
            echo "Erro: A opção -$OPTARG requer um argumento." >&2
            uso
            ;;
    esac
done

# --- Deslocando Argumentos Posicionais ---
# Após getopts terminar, OPTIND contém o índice do primeiro argumento não opcional.
# Usamos shift para descartar todas as opções analisadas, deixando apenas argumentos posicionais ($1, $2, etc.).
shift $((OPTIND - 1))

# Verifica se o argumento posicional obrigatório (ENTRADA) está presente
DADOS_ENTRADA="$1"
if [ -z "$DADOS_ENTRADA" ]; then
    echo "Erro: Dados de entrada obrigatórios."
    uso
fi

echo "---"
echo "Processando entrada: $DADOS_ENTRADA"
echo "Status verboso: $VERBOSO"

Dica: Sempre use shift $((OPTIND - 1)) imediatamente após o loop while getopts para separar claramente as opções dos argumentos posicionais restantes.

Lidando com Opções Longas como --option

O getopts embutido do Bash puro só suporta opções curtas. Para lidar com opções longas modernas (por exemplo, --verbose, --output-file=data.log), você deve implementar um loop de análise personalizado usando while e case com o comando shift.

Este método requer gerenciamento de argumentos mais explícito.

Analisador de Opções Longas Personalizado

#!/bin/bash

# --- Padrões ---
VERBOSO=0
ARQUIVO_SAIDA=""

# Função personalizada para exibir uso (omitida por brevidade)
# uso() { ... }

while [ "$#" -gt 0 ]; do
    case "$1" in
        --verbose)
            VERBOSO=1
            shift
            ;;
        --output-file)
            # Requer dois shifts: um para a flag, um para o valor
            if [ -z "${2:-}" ]; then
                echo "Erro: --output-file requer um valor."
                exit 1
            fi
            ARQUIVO_SAIDA="$2"
            shift 2
            ;;
        --help)
            uso
            ;;
        -*)
            echo "Erro: Opção desconhecida $1" >&2
            exit 1
            ;;
        *)
            # Encontrou o primeiro argumento posicional, para de analisar opções
            break
            ;;
    esac
done

# Os argumentos restantes agora estão disponíveis como $1, $2, etc.
# ... A lógica do script continua ...

if [ "$ARQUIVO_SAIDA" ]; then
    echo "Dados redirecionados para $ARQUIVO_SAIDA"
fi

Se seu script aceita argumentos posicionais após as opções, continue analisando até encontrar -- ou o primeiro argumento não opcional. Uma convenção comum é deixar -- significar "pare de analisar opções agora":

while [ "$#" -gt 0 ]; do
    case "$1" in
        --)
            shift
            break
            ;;
        --verbose)
            VERBOSO=1
            shift
            ;;
        *)
            break
            ;;
    esac
done

Avançado: Lidando com Opções Longas Chave-Valor (--key=value)

Se você prefere o estilo UNIX padrão onde os argumentos são passados usando um sinal de igual, você precisa usar substituição de parâmetros para dividir o argumento.

while [ "$#" -gt 0 ]; do
    case "$1" in
        --limit=*)
            VALOR_LIMITE="${1#*=}" # Remove tudo até e incluindo '='
            echo "Limite definido como: $VALOR_LIMITE"
            shift
            ;;
        # ... outras opções ...
    esac
done

Melhores Práticas para Scripts Robusto

A. Combinando Opções Curtas e Longas

Para máxima flexibilidade, scripters experientes frequentemente combinam a confiabilidade do getopts para opções curtas com o loop while/case personalizado para opções longas. O loop de opções longas é executado primeiro, consumindo flags longas, e então os argumentos restantes (incluindo opções curtas) são processados pelo getopts.

No entanto, um padrão comum e mais limpo é processar todos os argumentos em um loop personalizado robusto que procura tanto -o quanto --option, deslocando conforme necessário.

B. Tratamento de Erros e Uso

Sempre forneça uma função uso clara que explique os argumentos e opções obrigatórios do script. Esta função deve ser chamada quando:

  1. O usuário solicita ajuda (por exemplo, -h ou --help).
  2. Um argumento obrigatório está faltando.
  3. Uma opção inválida ou desconhecida é fornecida.

Certifique-se de que o script saia com um status diferente de zero em caso de erro (exit 1) e envie mensagens de erro para a Saída de Erro Padrão (>&2).

C. Valores Padrão

Inicialize todas as variáveis de configuração no início do script com valores padrão sensatos. Isso torna seu script previsível mesmo se nenhum argumento opcional for passado.

# Sempre inicialize variáveis antes de analisar
NIVEL_LOG="info"
FORCAR=0

Conclusão

Use argumentos posicionais para valores obrigatórios simples, getopts para opções curtas portáteis e um loop while/case personalizado quando precisar de opções longas. Teste valores ausentes, flags desconhecidas e argumentos com espaços antes de confiar no script em automação.