getopts: Analisando Opções de Linha de Comando no Bash

getopts: Analisando Opções de Linha de Comando no Bash

O Que Você Vai Aprender

  • Como analisar argumentos de opção como -a e -b valor em scripts shell de forma segura
  • O papel e a sintaxe de getopts optstring / OPTARG / OPTIND
  • Como tratar opções inválidas e argumentos ausentes por conta própria (modo silencioso)
  • Quando usar getopts (builtin do shell) vs getopt (comando externo)

Resumo Rápido

  • Prefira getopts em vez de análise manual (POSIX, portável)
  • O loop principal e while getopts ":a:bc" opt; do ... done
  • Opções que recebem argumento são declaradas como letra: na optstring (a:)
  • Após o loop, execute shift $((OPTIND - 1)) para alcançar os argumentos posicionais restantes

Premissas (ambiente alvo)

  • bash / POSIX sh (getopts é um builtin)
  • Apenas opções curtas (-a, -v) são suportadas. Opções longas como --verbose não são.

O Que é getopts?

Conclusão: getopts é um builtin do shell para análise de opções; combinado com um loop while, ele extrai argumentos -a / -b valor um de cada vez.

getopts é um builtin do shell que analisa opções de linha de comando (argumentos curtos com -) uma de cada vez. Em vez de desmontar $1 e $2 manualmente com uma instrução case, você obtém uma forma padronizada de processar opções.

A sintaxe básica é:

getopts optstring name [args]
  • optstring: o conjunto de letras de opção válidas (ex.: "abc" significa -a -b -c)
  • name: a variável que recebe a letra da opção detectada
  • Cada chamada processa uma opção, retornando status de saída 0 (verdadeiro) enquanto houver opções restantes

Esse comportamento de "retorna verdadeiro enquanto houver opções" é exatamente o que um loop while precisa.

Por que usar getopts em vez de análise manual?

Conclusão: getopts trata opções agrupadas (-abc) e o terminador -- de acordo com o padrão, algo que instruções case escritas manualmente tendem a ignorar.

Crie seu próprio while [ $# -gt 0 ] com case "$1" in e você acabará reimplementando cada regra sutil por conta própria:

  • Opções agrupadas: permitir que -a -b -c seja escrito como -abc
  • Opções com argumentos: aceitar tanto -b valor quanto -bvalor
  • O terminador --: parar de tratar tudo depois dele como opções
  • Detectar opções inválidas e argumentos ausentes

getopts trata tudo isso com comportamento padrão POSIX. É portável e evita sintaxe exclusiva do bash, funcionando sem alterações em um script #!/bin/sh.

Se opções longas (--output file) não são um requisito obrigatório, use getopts primeiro. O código é mais curto e o comportamento é padronizado.

Como usar getopts?

Conclusão: Declare as letras aceitas na optstring, adicione : às letras que recebem argumento; OPTARG armazena o valor e OPTIND o próximo índice.

Escrevendo a optstring

A optstring é uma string de letras de opção aceitas. Adicione : a uma letra que recebe argumento.

optstring Significado
"abc" -a -b -c (sem argumentos)
"a:bc" -a recebe argumento; -b -c não recebem
":a:bc" : inicial habilita modo silencioso (abaixo)

Template básico

#!/bin/bash

verbose=0
output=""

while getopts "vo:" opt; do
  case "$opt" in
    v) verbose=1 ;;
    o) output="$OPTARG" ;;
    \?) echo "Unknown option: -$OPTARG" >&2; exit 1 ;;
  esac
done

shift $((OPTIND - 1))

echo "verbose=$verbose output=$output"
echo "Remaining arguments: $*"

Exemplo de execução:

$ ./script.sh -v -o result.txt input1 input2
verbose=1 output=result.txt
Remaining arguments: input1 input2

OPTARG e OPTIND

getopts atualiza duas variáveis automaticamente durante a análise.

  • OPTARG: o valor do argumento de uma opção que recebe um (o:). Acima, é result.txt de -o result.txt.
  • OPTIND: o índice do próximo argumento a processar. Começa em 1. Após o loop, aponta para o primeiro argumento que não é opção.

Executar shift $((OPTIND - 1)) após o loop remove todas as opções processadas, deixando apenas os argumentos que não são opções (nomes de arquivo, etc.) em $1 em diante.

OPTIND é inicializado como 1 quando o shell inicia. Em uma função que executa o loop getopts mais de uma vez no mesmo shell, reinicie OPTIND=1 antes do loop ou a segunda passagem se comportará incorretamente.

Como tratar erros?

Conclusão: Um : inicial na optstring habilita o modo silencioso; opções inválidas colocam ? em name, argumentos ausentes colocam :, e OPTARG armazena a letra infratora.

getopts tem dois modos de relato de erros.

Modo normal (optstring não começa com :)

Em uma opção inválida ou argumento ausente, getopts imprime uma mensagem no erro padrão por conta própria e armazena ? em name. Conveniente, mas você não pode controlar o texto.

Modo silencioso (optstring começa com :)

Inicie a optstring com um : inicial (ex.: ":vo:") e getopts não imprime mensagem, permitindo que seu script trate cada erro. O comportamento é:

Situação Valor em name Valor em OPTARG
Opção inválida ? o caractere infrator
Argumento ausente : a letra da opção que precisava de um

Tratamento de erros no modo silencioso:

while getopts ":vo:" opt; do
  case "$opt" in
    v) verbose=1 ;;
    o) output="$OPTARG" ;;
    \?) echo "Unknown option: -$OPTARG" >&2; exit 1 ;;
    :)  echo "Option -$OPTARG requires an argument" >&2; exit 1 ;;
  esac
done

Prefira o modo silencioso na prática. Você obtém controle total da saída para o usuário: localizar mensagens, chamar uma função usage() e encerrar de forma limpa.

Como getopts difere de getopt?

Conclusão: getopts é um builtin apenas para opções curtas; se você precisa de opções longas como --verbose, use o getopt externo (util-linux).

Eles diferem por uma letra, mas são ferramentas diferentes.

Aspecto getopts (builtin) getopt (externo)
O que é Builtin do shell /usr/bin/getopt (util-linux, etc.)
Opções longas Não (-a apenas) Sim (--all)
Portabilidade Alta, padrão POSIX Varia (versão GNU aprimorada pode ser necessária)
Uso Busca uma de cada vez no loop Reordena todos os args, depois analisa

Se opções curtas são suficientes, use getopts; se opções no estilo --output são um requisito, considere getopt (a versão GNU aprimorada).

Existe um truque para tratar opções longas apenas com getopts (tratar - como uma opção que recebe argumento e reanalisar OPTARG), mas isso prejudica a legibilidade. Quando o requisito é firme, o getopt puro é mais fácil de manter.

Quais são as armadilhas comuns?

Conclusão: Esquecer de reiniciar OPTIND, esquecer o shift e passar opções longas são os três clássicos.

1. Não reiniciar OPTIND dentro de uma função

Usar getopts em uma função carrega o valor anterior de OPTIND. Adicione local OPTIND (ou OPTIND=1) antes do loop.

parse_args() {
  local OPTIND   # local da funcao, reinicia a cada chamada
  while getopts ":vo:" opt; do
    # ...
  done
}

2. Esquecer o shift e desalinhar argumentos posicionais

Pule shift $((OPTIND - 1)) e $1 permanece como uma string de opção, quebrando o tratamento de arquivos posterior. Sempre execute logo após o loop.

3. Passar opções longas

Passe --verbose para getopts e ele interpretará cada caractere após - (-, v, e...) como uma opção separada, causando comportamento indesejado. Se você precisa de opções longas, escolha getopt na fase de projeto.

Resumo

getopts implementa a análise de opções do shell de forma padronizada e portável como builtin. Memorize a forma while getopts ":a:bc" opt; do case ... done, trate erros por conta própria no modo silencioso (: inicial) e finalize com shift $((OPTIND - 1)) para alcançar os argumentos posicionais -- domine esses três pontos e você estará pronto para scripts do mundo real.

Próximas Leituras