
Existe um getopt
comando na linha de comando do bash. getopt
pode ser usado com opções curtas (como getopt -o axby "$@"
) e pode ser usado com opções curtas e longas (como getopt -o axby -l long-key -- "$@"
), mas agora precisoapenasopções longas (ou seja, opções curtas não existem), porém o comando getopt -l long-key -- "$@"
não analisa --long-key
a opção corretamente. Então, como posso usar getopt
o comando comapenasopções longas? Ou é impossível ou é apenas um bug do getopt
comando?
Responder1
getopt
está perfeitamente bem em não ter opções curtas. Mas você precisa dizer que não tem opções curtas. É uma peculiaridade na sintaxe - do manual:
Se no
-o
ou--options
option for encontrado na primeira parte, o primeiro parâmetro da segunda parte será usado como a sequência curta de opções.
É isso que está acontecendo no seu teste: getopt -l long-key -- --long-key foo
trata --long-key
como lista de opções -egklnoy
e foo
como único argumento. Usar
getopt -o '' -l long-key -- "$@"
por exemplo
$ getopt -l long-key -o '' -- --long-key foo
--long-key -- 'foo'
$ getopt -l long-key -o '' -- --long-key --not-recognized -n foo
getopt: unrecognized option '--not-recognized'
getopt: invalid option -- 'n'
--long-key -- 'foo'
Responder2
Não sei, getopt
mas o getopts
builtin pode ser usado para lidar apenas com opções longas como esta:
while getopts :-: o
do case "$o$OPTARG" in
(-longopt1) process ;;
(-longopt2) process ;;
esac; done
É claro que, tal como está, isso não funciona se as opções longas tiverem argumentos. Isso pode ser feito, mas, como aprendi trabalhando nisso. Embora inicialmente o tenha incluído aqui, percebi que para opções longas ele não tem muita utilidade. Nesse caso, estava apenas encurtando meus case
(match)
campos em um caractere único e previsível. Agora, o que eu sei é que é excelente para opções curtas - é mais útil quando está fazendo um loop em uma string de comprimento desconhecido e selecionando bytes únicos de acordo com sua string de opções. Mas quando a opçãoéo argumento, não há muito que você esteja fazendo com uma for var do case $var in
combinação que ela possa fazer. É melhor, penso eu, manter as coisas simples.
Suspeito que o mesmo seja verdade, getopt
mas não sei o suficiente para dizer com certeza. Dada a matriz de argumentos a seguir, demonstrarei meu próprio pequeno analisador de argumentos - que depende principalmente do relacionamento de avaliação/atribuição que passei a apreciar para alias
e $((shell=math))
.
set -- this is ignored by default --lopt1 -s 'some '\''
args' here --ignored and these are ignored \
--alsoignored andthis --lopt2 'and
some "`more' --lopt1 and just a few more
Essa é a string arg com a qual trabalharei. Agora:
aopts() { env - sh -s -- "$@"
} <<OPTCASE 3<<\OPTSCRIPT
acase() case "\$a" in $(fmt='
(%s) f=%s; aset "?$(($f)):";;\n'
for a do case "$a" in (--) break;;
(--*[!_[:alnum:]]*) continue;;
(--*) printf "$fmt" "$a" "${a#--}";;
esac;done;printf "$fmt" '--*' ignored)
(*) aset "" "\$a";;esac
shift "$((SHIFT$$))"; f=ignored; exec <&3
OPTCASE
aset() { alias "$f=$(($f${1:-=$(($f))+}1))"
[ -n "${2+?}" ] && alias "${f}_$(($f))=$2"; }
for a do acase; done; alias
#END
OPTSCRIPT
Isso processa o array arg de uma de duas maneiras diferentes, dependendo se você entrega a ele um ou dois conjuntos de argumentos separados pelo --
delimitador. Em ambos os casos, aplica-se a sequências de processamento para o array arg.
Se você chamar assim:
: $((SHIFT$$=3)); aopts --lopt1 --lopt2 -- "$@"
Sua primeira tarefa será escrever sua acase()
função para se parecer com:
acase() case "$a" in
(--lopt1) f=lopt1; aset "?$(($f)):";;
(--lopt2) f=lopt2; aset "?$(($f)):";;
(--*) f=ignored; aset "?$(($f)):";;
(*) aset "" "$a";;esac
E ao lado shift 3
. A substituição de comando na acase()
definição da função é avaliada quando o shell de chamada constrói os documentos de entrada da função aqui, mas acase()
nunca é chamado ou definido no shell de chamada. Ele é chamado no subshell, é claro, e dessa forma você pode especificar dinamicamente as opções de interesse na linha de comando.
Se você entregar um array não delimitado, ele simplesmente preencherá acase()
com correspondências para todos os argumentos que começam com string --
.
A função faz praticamente todo o seu processamento no subshell - salvando iterativamente cada um dos valores do argumento em aliases atribuídos com nomes associativos. Quando termina, ele imprime todos os valores salvos alias
- que são especificados pelo POSIX para imprimir todos os valores salvos citados de forma que seus valores possam ser reinseridos no shell. Então quando eu faço...
aopts --lopt1 --lopt2 -- "$@"
Sua saída é semelhante a esta:
...ignored...
lopt1='8'
lopt1_1='-s'
lopt1_2='some '\'' args'
lopt1_3='here'
lopt1_4='and'
lopt1_5='just'
lopt1_6='a'
lopt1_7='few'
lopt1_8='more'
lopt2='1'
lopt2_1='and
some "`more'
À medida que percorre a lista de argumentos, ele verifica se há uma correspondência no bloco case. Se encontrar uma correspondência, ele lança uma bandeira - f=optname
. Até encontrar novamente uma opção válida, ele adicionará cada argumento subsequente a um array que constrói com base no sinalizador atual. Se a mesma opção for especificada várias vezes, os resultados serão compostos e não serão substituídos. Qualquer coisa que não esteja no caso - ou quaisquer argumentos após opções ignoradas - são atribuídos a umignoradovariedade.
A saída é protegida pelo shell para entrada do shell automaticamente pelo shell, e assim:
eval "$(: $((SHIFT$$=3));aopts --lopt1 --lopt2 -- "$@")"
...deve ser perfeitamente seguro. Se por algum motivonão éseguro, então você provavelmente deve enviar um relatório de bug ao mantenedor do shell.
Ele atribui dois tipos de valores de alias para cada correspondência. Primeiro, ele define um sinalizador - isso ocorre independentemente de uma opção preceder ou não argumentos não correspondentes. Portanto, qualquer ocorrência --flag
na lista de argumentos será acionada flag=1
. Isso não é composto - --flag --flag --flag
apenas é obtido flag=1
. Este valorfazincremento embora - para quaisquer argumentos que possam segui-lo. Pode ser usado como uma chave de índice. Depois de fazer o eval
acima, posso fazer:
printf %s\\n "$lopt1" "$lopt2"
...obter...
8
1
E assim:
for o in lopt1 lopt2
do list= i=0; echo "$o = $(($o))"
while [ "$((i=$i+1))" -le "$(($o))" ]
do list="$list $o $i \"\${${o}_$i}\" "
done; eval "printf '%s[%02d] = %s\n' $list"; done
SAÍDA
lopt1 = 8
lopt1[01] = -s
lopt1[02] = some ' args
lopt1[03] = here
lopt1[04] = and
lopt1[05] = just
lopt1[06] = a
lopt1[07] = few
lopt1[08] = more
lopt2 = 1
lopt2[01] = and
some "`more
E para argumentos que não correspondessem eu substituiriaignoradono campo acima for ... in
para obter:
ignored = 10
ignored[01] = this
ignored[02] = is
ignored[03] = ignored
ignored[04] = by
ignored[05] = default
ignored[06] = and
ignored[07] = these
ignored[08] = are
ignored[09] = ignored
ignored[10] = andthis