Getopts: parsing de opções como um profissional
1. Introdução ao Getopts e por que usá-lo
Desenvolver scripts Bash que aceitam opções de linha de comando é uma necessidade frequente. Muitos iniciantes recorrem ao parsing manual com $1, $2 e estruturas case para interpretar argumentos. Essa abordagem, embora funcional para scripts simples, rapidamente se torna frágil e difícil de manter quando o número de opções cresce.
# Abordagem manual problemática
case "$1" in
-v) VERBOSE=true; shift ;;
-o) OUTPUT="$2"; shift 2 ;;
*) echo "Opção inválida"; exit 1 ;;
esac
O getopts resolve esses problemas oferecendo uma solução padronizada e robusta. Suas principais vantagens incluem: parsing automático de opções agrupadas (ex: -abc), detecção de opções ausentes, suporte a argumentos obrigatórios e tratamento de erros integrado.
A sintaxe básica do getopts segue este padrão:
while getopts "opcoes" var; do
case "$var" in
opcao) ação ;;
esac
done
2. Estrutura fundamental do loop com getopts
O getopts utiliza variáveis especiais para controlar o processo de parsing. A variável OPTIND (option index) armazena o índice do próximo argumento a ser processado. Já OPTARG contém o valor do argumento quando a opção espera um parâmetro.
A string de opções define o comportamento esperado. Opções seguidas de : indicam que esperam um argumento obrigatório:
while getopts "vo:" opt; do
case "$opt" in
v) echo "Modo verboso ativado" ;;
o) echo "Arquivo de saída: $OPTARG" ;;
esac
done
Neste exemplo, v não espera argumento, enquanto o espera um valor. A diferença é crucial: sem os : adequados, o script pode falhar silenciosamente ou interpretar argumentos incorretamente.
3. Tipos de opções: sem argumento, com argumento obrigatório e opcional
Opções booleanas são as mais simples, funcionando como flags de ativação/desativação:
while getopts "vd" opt; do
case "$opt" in
v) VERBOSE=true ;;
d) DEBUG=true ;;
esac
done
Opções com argumento obrigatório exigem o : após a letra na string de opções:
while getopts "o:u:" opt; do
case "$opt" in
o) ARQUIVO="$OPTARG" ;;
u) USUARIO="$OPTARG" ;;
esac
done
Uma limitação importante do getopts nativo é a ausência de suporte a argumentos opcionais. Para contornar isso, é possível implementar uma lógica manual dentro do case:
while getopts "c:" opt; do
case "$opt" in
c) if [[ "$OPTARG" =~ ^- ]]; then
CONFIG="default.cfg"
OPTIND=$((OPTIND - 1))
else
CONFIG="$OPTARG"
fi ;;
esac
done
4. Tratamento de erros e validação de entrada
Por padrão, o getopts exibe mensagens de erro do shell para opções inválidas. Para suprimir essas mensagens e implementar um tratamento personalizado, adicione : no início da string de opções:
while getopts ":vo:" opt; do
case "$opt" in
v) VERBOSE=true ;;
o) OUTPUT="$OPTARG" ;;
\?) echo "Opção inválida: -$OPTARG" >&2 ;;
:) echo "Opção -$OPTARG requer um argumento" >&2 ;;
esac
done
A validação de valores recebidos é essencial para scripts robustos:
case "$opt" in
n) if ! [[ "$OPTARG" =~ ^[0-9]+$ ]]; then
echo "Erro: -n requer um número" >&2
exit 1
fi
NUMERO="$OPTARG" ;;
esac
5. Combinando opções e argumentos posicionais
Após processar todas as opções com getopts, use shift $((OPTIND - 1)) para remover as opções processadas e deixar apenas os argumentos posicionais:
while getopts "vo:" opt; do
case "$opt" in
v) VERBOSE=true ;;
o) OUTPUT="$OPTARG" ;;
esac
done
shift $((OPTIND - 1))
# Agora $@ contém apenas argumentos posicionais
for arquivo in "$@"; do
echo "Processando: $arquivo"
done
A ordem correta de uso é: opções primeiro, argumentos posicionais depois. O script deve ser chamado como ./script.sh -v -o saida.txt arquivo1.txt arquivo2.txt.
6. Padrões de design e boas práticas
Definir valores padrão antes do loop de parsing é uma prática recomendada:
VERBOSE=false
OUTPUT_FILE=""
CONFIG_FILE="default.conf"
while getopts ":vo:c:h" opt; do
case "$opt" in
v) VERBOSE=true ;;
o) OUTPUT_FILE="$OPTARG" ;;
c) CONFIG_FILE="$OPTARG" ;;
h) mostrar_ajuda; exit 0 ;;
esac
done
Implementar uma função de ajuda melhora a usabilidade:
mostrar_ajuda() {
cat << EOF
Uso: $0 [OPÇÕES] [ARQUIVOS...]
Opções:
-v Modo verboso
-o ARQ Arquivo de saída
-c ARQ Arquivo de configuração
-h Mostra esta ajuda
EOF
}
7. Limitações do getopts e alternativas avançadas
A principal limitação do getopts é a ausência de suporte nativo a opções longas como --verbose. Uma solução híbrida combina getopts para opções curtas com parsing manual para longas:
while [[ $# -gt 0 ]]; do
case "$1" in
--verbose) VERBOSE=true; shift ;;
--output=*) OUTPUT="${1#*=}"; shift ;;
-*) while getopts "vo:" opt "$1"; do
case "$opt" in
v) VERBOSE=true ;;
o) OUTPUT="$OPTARG" ;;
esac
done
shift ;;
*) break ;;
esac
done
Para projetos mais complexos, considere ferramentas externas como o getopt GNU (que suporta opções longas) ou bibliotecas como argbash para geração automática de código de parsing.
8. Exemplo completo: script profissional com getopts
#!/bin/bash
# Script de backup profissional com getopts
# Uso: ./backup.sh [-v] [-d DESTINO] [-c CONFIG] ARQUIVO...
# Valores padrão
VERBOSE=false
DESTINO="./backup"
CONFIG="backup.conf"
# Função de ajuda
mostrar_ajuda() {
cat << EOF
Uso: $0 [OPÇÕES] ARQUIVO...
Opções:
-v Modo verboso
-d DIR Diretório de destino (padrão: ./backup)
-c ARQ Arquivo de configuração (padrão: backup.conf)
-h Mostra esta ajuda
Exemplo:
$0 -v -d /tmp/backup -c meu.conf arquivo1.txt arquivo2.txt
EOF
}
# Parsing de opções com tratamento de erros
while getopts ":vd:c:h" opt; do
case "$opt" in
v) VERBOSE=true ;;
d) DESTINO="$OPTARG" ;;
c) CONFIG="$OPTARG" ;;
h) mostrar_ajuda; exit 0 ;;
\?) echo "Erro: opção inválida -$OPTARG" >&2; exit 1 ;;
:) echo "Erro: opção -$OPTARG requer argumento" >&2; exit 1 ;;
esac
done
shift $((OPTIND - 1))
# Validação de argumentos posicionais
if [[ $# -eq 0 ]]; then
echo "Erro: nenhum arquivo especificado" >&2
mostrar_ajuda
exit 1
fi
# Validação do destino
if [[ ! -d "$DESTINO" ]]; then
$VERBOSE && echo "Criando diretório: $DESTINO"
mkdir -p "$DESTINO" || { echo "Erro ao criar destino" >&2; exit 1; }
fi
# Processamento dos arquivos
for arquivo in "$@"; do
if [[ ! -f "$arquivo" ]]; then
echo "Aviso: arquivo não encontrado: $arquivo" >&2
continue
fi
$VERBOSE && echo "Copiando: $arquivo -> $DESTINO"
cp "$arquivo" "$DESTINO/" || echo "Erro ao copiar $arquivo" >&2
done
$VERBOSE && echo "Backup concluído em $DESTINO"
Referências
- GNU Bash Manual: Builtins - getopts — Documentação oficial do Bash sobre o comando
getopts, com detalhes de sintaxe e comportamento. - Advanced Bash-Scripting Guide: getopts — Guia avançado com exemplos práticos e explicações detalhadas sobre o uso de
getoptsem scripts. - Shell Scripting Tutorial: getopts — Tutorial passo a passo com exemplos comentados e boas práticas para parsing de opções.
- Bash Hackers Wiki: getopts — Wiki colaborativa com tutorial completo, incluindo tratamento de erros e truques avançados.
- Linuxize: Bash getopts Tutorial — Tutorial moderno com exemplos práticos de scripts reais usando
getopts. - Stack Overflow: How to use getopts in Bash — Discussão com exemplos variados e soluções para problemas comuns com
getopts.