getopt
e getopts
são bestas diferentes, e as pessoas parecem ter um pouco de mal-entendido do que fazem. getopts
é um comando interno para bash
processar opções de linha de comando em um loop e atribuir cada opção e valor encontrado, por sua vez, a variáveis internas, para que você possa processá-las ainda mais. getopt
, no entanto, é um programa utilitário externo e, na verdade, não processa suas opções para você da maneira que, por exemplo getopts
, o bash , o Getopt
módulo Perl ou o Python optparse
/ argparse
modules. Tudo o que getopt
faz é canonizar as opções passadas - ou seja, convertê-las para um formato mais padrão, para que seja mais fácil para um script de shell processá-las. Por exemplo, um aplicativo de getopt
pode converter o seguinte:
myscript -ab infile.txt -ooutfile.txt
nisso:
myscript -a -b -o outfile.txt infile.txt
Você precisa fazer o processamento real sozinho. Você não precisa usargetopt
-lo se fizer várias restrições na maneira de especificar opções:
- coloque apenas uma opção por argumento;
- todas as opções vão antes de qualquer parâmetro posicional (isto é, argumentos sem opção);
- para opções com valores (por exemplo,
-o
acima), o valor deve ser apresentado como um argumento separado (após um espaço).
Por que usar em getopt
vez de getopts
? O motivo básico é que apenas o GNU getopt
oferece suporte para opções de linha de comando com nomes longos. 1 (GNU getopt
é o padrão no Linux. O Mac OS X e o FreeBSD vêm com uma funcionalidade básica e não muito útilgetopt
, mas a versão do GNU pode ser instalada; veja abaixo.)
Por exemplo, aqui está um exemplo de uso do GNU getopt
, de um script meu chamado javawrap
:
# NOTE: This requires GNU getopt. On Mac OS X and FreeBSD, you have to install this
# separately; see below.
TEMP=`getopt -o vdm: --long verbose,debug,memory:,debugfile:,minheap:,maxheap: \
-n 'javawrap' -- "$@"`
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"
VERBOSE=false
DEBUG=false
MEMORY=
DEBUGFILE=
JAVA_MISC_OPT=
while true; do
case "$1" in
-v | --verbose ) VERBOSE=true; shift ;;
-d | --debug ) DEBUG=true; shift ;;
-m | --memory ) MEMORY="$2"; shift 2 ;;
--debugfile ) DEBUGFILE="$2"; shift 2 ;;
--minheap )
JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MinHeapFreeRatio=$2"; shift 2 ;;
--maxheap )
JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MaxHeapFreeRatio=$2"; shift 2 ;;
-- ) shift; break ;;
* ) break ;;
esac
done
Isso permite especificar opções como --verbose -dm4096 --minh=20 --maxhe 40 --debugfi="/Users/John Johnson/debug.txt"
ou semelhante. O efeito da chamada para getopt
é canonizar as opções para --verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug.txt"
que você possa processá-las mais facilmente. A citação ao redor"$1"
e "$2"
é importante, pois garante que os argumentos com espaços sejam tratados adequadamente.
Se você excluir as 9 primeiras linhas (tudo na eval set
linha), o código ainda funcionará ! No entanto, seu código será muito mais exigente em quais tipos de opções ele aceita: Em particular, você precisará especificar todas as opções no formulário "canônico" descrito acima. Com o uso de getopt
, no entanto, você pode opções único grupo letras, usar mais curtos formas não ambíguas de longas-opções, utilize o --file foo.txt
ou --file=foo.txt
estilo, utilize o -m 4096
ou -m4096
estilo, misturar opções e não-opções em qualquer ordem, etc. getopt
também gera uma mensagem de erro se forem encontradas opções não reconhecidas ou ambíguas.
NOTA : Na verdade, existem duas versões totalmente diferentesgetopt
, básica getopt
e GNU getopt
, com recursos diferentes e convenções de chamada diferentes. 2 O Basic getopt
está bastante quebrado: ele não apenas lida com opções longas, como também não consegue lidar com espaços incorporados dentro de argumentos ou argumentos vazios, enquanto getopts
faz isso corretamente. O código acima não funcionará no básico getopt
. O GNU getopt
é instalado por padrão no Linux, mas no Mac OS X e FreeBSD ele precisa ser instalado separadamente. No Mac OS X, instale o MacPorts ( http://www.macports.org ) e, em seguida, sudo port install getopt
instale o GNU getopt
(geralmente no . No FreeBSD, instale ./opt/local/bin
), e verifique se /opt/local/bin
está no caminho do shell antes de/usr/bin
misc/getopt
Um guia rápido para modificar o código de exemplo para o seu próprio programa: Das primeiras linhas, tudo é "padrão" que deve permanecer o mesmo, exceto a linha que chama getopt
. Você deve alterar o nome do programa depois -n
, especificar opções curtas depois -o
e opções longas depois --long
. Coloque dois pontos depois das opções que levam um valor.
Finalmente, se você vir um código que acabou de ser set
substituído eval set
, ele foi escrito para o BSD getopt
. Você deve alterá-lo para usar o eval set
estilo, que funciona bem com as duas versões getopt
, enquanto o plain set
não funciona corretamente com o GNU getopt
.
1 Na verdade, getopts
in ksh93
suporta opções de nomes longos, mas esse shell não é usado com tanta frequência quanto bash
. Em zsh
, use zparseopts
para obter essa funcionalidade.
2 Tecnicamente, "GNU getopt
" é um nome impróprio; esta versão foi realmente escrita para Linux e não para o projeto GNU. No entanto, segue todas as convenções do GNU, e o termo "GNU getopt
" é comumente usado (por exemplo, no FreeBSD).