getopts: Analisando Opcoes de Linha de Comando no Bash

getopts: Analisando Opcoes de Linha de Comando no Bash

O Que Voce Vai Aprender

  • Como analisar argumentos de opcao como -a e -b valor em scripts shell de forma segura
  • O papel e a sintaxe de getopts optstring / OPTARG / OPTIND
  • Como tratar opcoes invalidas e argumentos ausentes por conta propria (modo silencioso)
  • Quando usar getopts (builtin do shell) vs getopt (comando externo)

Resumo Rapido

  • Prefira getopts em vez de analise manual (POSIX, portavel)
  • O loop principal e while getopts ":a:bc" opt; do ... done
  • Opcoes que recebem argumento sao declaradas como letra: na optstring (a:)
  • Apos o loop, execute shift $((OPTIND - 1)) para alcancar os argumentos posicionais restantes

Premissas (ambiente alvo)

  • bash / POSIX sh (getopts e um builtin)
  • Apenas opcoes curtas (-a, -v) sao suportadas. Opcoes longas como --verbose nao sao.

O Que e getopts?

Conclusao: getopts e um builtin do shell para analise de opcoes; combinado com um loop while, ele extrai argumentos -a / -b valor um de cada vez.

getopts e um builtin do shell que analisa opcoes de linha de comando (argumentos curtos com -) uma de cada vez. Em vez de desmontar $1 e $2 manualmente com uma instrucao case, voce obtem uma forma padronizada de processar opcoes.

A sintaxe basica e:

getopts optstring name [args]
  • optstring: o conjunto de letras de opcao validas (ex.: "abc" significa -a -b -c)
  • name: a variavel que recebe a letra da opcao detectada
  • Cada chamada processa uma opcao, retornando status de saida 0 (verdadeiro) enquanto houver opcoes restantes

Esse comportamento de "retorna verdadeiro enquanto houver opcoes" e exatamente o que um loop while precisa.

Por que usar getopts em vez de analise manual?

Conclusao: getopts trata opcoes agrupadas (-abc) e o terminador -- de acordo com o padrao, algo que instrucoes case escritas manualmente tendem a ignorar.

Crie seu proprio while [ $# -gt 0 ] com case "$1" in e voce acabara reimplementando cada regra sutil por conta propria:

  • Opcoes agrupadas: permitir que -a -b -c seja escrito como -abc
  • Opcoes com argumentos: aceitar tanto -b valor quanto -bvalor
  • O terminador --: parar de tratar tudo depois dele como opcoes
  • Detectar opcoes invalidas e argumentos ausentes

getopts trata tudo isso com comportamento padrao POSIX. E portavel e evita sintaxe exclusiva do bash, funcionando sem alteracoes em um script #!/bin/sh.

Se opcoes longas (--output file) nao sao um requisito obrigatorio, use getopts primeiro. O codigo e mais curto e o comportamento e padronizado.

Como usar getopts?

Conclusao: Declare as letras aceitas na optstring, adicione : as letras que recebem argumento; OPTARG armazena o valor e OPTIND o proximo indice.

Escrevendo a optstring

A optstring e uma string de letras de opcao aceitas. Adicione : a uma letra que recebe argumento.

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

Template basico

#!/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 execucao:

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

OPTARG e OPTIND

getopts atualiza duas variaveis automaticamente durante a analise.

  • OPTARG: o valor do argumento de uma opcao que recebe um (o:). Acima, e result.txt de -o result.txt.
  • OPTIND: o indice do proximo argumento a processar. Comeca em 1. Apos o loop, aponta para o primeiro argumento que nao e opcao.

Executar shift $((OPTIND - 1)) apos o loop remove todas as opcoes processadas, deixando apenas os argumentos que nao sao opcoes (nomes de arquivo, etc.) em $1 em diante.

OPTIND e inicializado como 1 quando o shell inicia. Em uma funcao que executa o loop getopts mais de uma vez no mesmo shell, reinicie OPTIND=1 antes do loop ou a segunda passagem se comportara incorretamente.

Como tratar erros?

Conclusao: Um : inicial na optstring habilita o modo silencioso; opcoes invalidas colocam ? em name, argumentos ausentes colocam :, e OPTARG armazena a letra infratora.

getopts tem dois modos de relato de erros.

Modo normal (optstring nao comeca com :)

Em uma opcao invalida ou argumento ausente, getopts imprime uma mensagem no erro padrao por conta propria e armazena ? em name. Conveniente, mas voce nao pode controlar o texto.

Modo silencioso (optstring comeca com :)

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

Situacao Valor em name Valor em OPTARG
Opcao invalida ? o caractere infrator
Argumento ausente : a letra da opcao 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 pratica. Voce obtem controle total da saida para o usuario: localizar mensagens, chamar uma funcao usage() e encerrar de forma limpa.

Como getopts difere de getopt?

Conclusao: getopts e um builtin apenas para opcoes curtas; se voce precisa de opcoes longas como --verbose, use o getopt externo (util-linux).

Eles diferem por uma letra, mas sao ferramentas diferentes.

Aspecto getopts (builtin) getopt (externo)
O que e Builtin do shell /usr/bin/getopt (util-linux, etc.)
Opcoes longas Nao (-a apenas) Sim (--all)
Portabilidade Alta, padrao POSIX Varia (versao GNU aprimorada pode ser necessaria)
Uso Busca uma de cada vez no loop Reordena todos os args, depois analisa

Se opcoes curtas sao suficientes, use getopts; se opcoes no estilo --output sao um requisito, considere getopt (a versao GNU aprimorada).

Existe um truque para tratar opcoes longas apenas com getopts (tratar - como uma opcao que recebe argumento e reanalisar OPTARG), mas isso prejudica a legibilidade. Quando o requisito e firme, o getopt puro e mais facil de manter.

Quais sao as armadilhas comuns?

Conclusao: Esquecer de reiniciar OPTIND, esquecer o shift e passar opcoes longas sao os tres classicos.

1. Nao reiniciar OPTIND dentro de uma funcao

Usar getopts em uma funcao 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 opcao, quebrando o tratamento de arquivos posterior. Sempre execute logo apos o loop.

3. Passar opcoes longas

Passe --verbose para getopts e ele interpretara cada caractere apos - (-, v, e...) como uma opcao separada, causando comportamento indesejado. Se voce precisa de opcoes longas, escolha getopt na fase de projeto.

Resumo

getopts implementa a analise de opcoes do shell de forma padronizada e portavel como builtin. Memorize a forma while getopts ":a:bc" opt; do case ... done, trate erros por conta propria no modo silencioso (: inicial) e finalize com shift $((OPTIND - 1)) para alcancar os argumentos posicionais -- domine esses tres pontos e voce estara pronto para scripts do mundo real.

Proximas Leituras