Existe um comando getopt
na linha de comando 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 preciso apenas das opções longas (ou seja, as opções curtas não existem), mas o comando getopt -l long-key -- "$@"
não analisa a opção --long-key
corretamente. Então, como posso usar o comando getopt
com apenas opções longas? Ou é impossível ou é apenas um bug do comando getopt
?
Comentários
Resposta
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 não
-o
ou--options
opção é encontrada na primeira parte, o primeiro parâmetro da segunda parte é usado como a string de opções curta.
É isso que está acontecendo em seu teste: getopt -l long-key -- --long-key foo
trata --long-key
como a lista de opções -egklnoy
e foo
como o único argumento. Use
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"
Comentários
- O OP ' s mistura de
getopts
egetopt
infectar sua resposta? Você começa comentandogetopts
e depois menciona apenasgetopt
. - @Anthon Todas as minhas respostas são sobre o
getopt
programa GNU coreutils, que é sobre o que a pergunta se trata. I corrigimos o texto que diziagetopts
. Obrigado.getopts
não ' nem faz opções longas, então nada disso se aplicaria agetopts
. - O OP originalmente tinha a tag
getopts
. Não queria mudar sua resposta, porque você normalmente sabe muito melhor do que eu sobre o que está escrevendo 🙂 - Perdi quase uma hora tentando descobrir isso. Você acabou de me poupar alguns goles de café em vão. Obrigado … vamos aproveitar melhor este café agora. ☕️
Resposta
Não sei sobre getopt
, mas a getopts
embutido 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, como está, isso não ” t funciona se as opções longas devem ter argumentos. Pode ser feito, mas, como aprendi trabalhando nisso. Embora eu inicialmente o incluísse aqui, percebi que para opções longas, ele não tem muita utilidade. Nesse caso, ele estava apenas encurtando meu case
(match)
campos por um único caractere 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ção . 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, Eu acho que, para mantê-lo simples.
Suspeito que o mesmo se aplica a getopt
, mas não sei o suficiente para dizer com certeza. Dada a seguinte matriz de arg, demonstrarei meu próprio analisador de arg – que depende principalmente da relação 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 de argumento com a qual irei trabalhar. 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 em uma de duas maneiras diferentes, dependendo se você lhe entrega um ou dois conjuntos de argumentos separados pelo delimitador --
. Em ambos os casos, ele se aplica a sequências de processamento para a matriz arg.
Se você chamá-lo assim:
: $((SHIFT$$=3)); aopts --lopt1 --lopt2 -- "$@"
Sua primeira ordem de o negócio 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 de shift 3
.A substituição de comando na acase()
definição da função é avaliada quando o shell de chamada cria os documentos-aqui de entrada da função, 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ê fornecer um array não delimitado, ele simplesmente preenche acase()
com correspondências para todos os argumentos que começam com a string --
.
A função faz praticamente todo o seu processamento no subshell – salvando iterativamente cada um dos valores de arg em aliases atribuídos com nomes associativos. Quando termina, ele imprime todos os valores salvos com alias
– que é especificado pelo POSIX para imprimir todos os valores salvos citados de forma que seus valores possam ser reintroduzidos no shell. Então, quando eu faço …
aopts --lopt1 --lopt2 -- "$@"
Sua saída é parecida com 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, verifica se há correspondência no bloco de caso. Se encontrar uma correspondência lá, ele lançará uma bandeira – f=optname
. Até que encontre novamente uma opção válida, ele adicionará cada arg subsequente a um array que ele 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 seguindo opções ignoradas – são atribuídos a um array ignorado .
A saída é protegida por shell para entrada de shell automaticamente pelo shell, e assim :
eval "$(: $((SHIFT$$=3));aopts --lopt1 --lopt2 -- "$@")"
… deve ser perfeitamente seguro. Se por alguma razão não for seguro, então você provavelmente deve enviar um relatório de bug ao seu mantenedor do shell.
Ele atribui dois tipos de aliases para cada combinação. Primeiro, ele define um sinalizador – isso ocorre quer uma opção preceda ou não os argumentos não correspondentes. Portanto, qualquer ocorrência de --flag
na lista de argumentos acionará flag=1
. Isso não se compõe – --flag --flag --flag
apenas obtém flag=1
. Este valor não incrementa – para quaisquer argumentos que possam segui-lo. Ele pode ser usado como uma chave de índice. Depois de fazer o eval
acima, posso fazer:
printf %s\\n "$lopt1" "$lopt2"
… para obter …
8 1
E então:
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 correspondam, eu substituiria ignorado no campo for ... in
acima 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
getopts
interno, mas você está usando o comando/usr/bin/getopt
.