Lembro-me de ter visto em algum lugar um bash
script usando case
e shift
para percorrer a lista de parâmetros posicionais, analisar sinalizadores e opções com argumentos quando os encontra e os remove após a análise para deixar apenas os argumentos simples, que são processados posteriormente pelo restante do roteiro.
Por exemplo, ao analisar a linha de comando cp -R file1 -t /mybackup file2 -f
, ele primeiro percorreria os parâmetros, reconheceria que o usuário solicitou a descida nos diretórios -R
, especificou o destino -t /mybackup
e forçou a cópia -f
e os remove da lista de parâmetros, deixando o programa para processar file1 file2
como os argumentos restantes.
Mas parece que não consigo me lembrar / descobrir o script que vi sempre. Eu só gostaria de poder fazer isso. Venho pesquisando em vários sites e anexo uma lista de páginas relevantes que examinei.
Uma pergunta neste site perguntou especificamente sobre "opções independentes de ordem", mas a resposta única e a resposta da pergunta para a qual foi enganada não considera casos como o acima, onde as opções são misturadas com argumentos normais, que eu presumo ser o razão para a pessoa mencionar especificamente opções independentes de pedidos .
Como bash
o built-in getopts
parece parar no primeiro argumento de não opção, ele não parece ser suficiente como solução. É por isso que a página do Wooledge BashFAQ (veja abaixo) explica como reorganizar os argumentos. Mas eu gostaria de evitar a criação de várias matrizes, caso a lista de argumentos seja bastante longa.
Como shift
não oferece suporte à exclusão de argumentos individuais no meio da lista de parâmetros, não tenho certeza de qual é uma maneira direta de implementar o que estou pedindo.
Gostaria de saber se alguém tem alguma solução para remover argumentos do meio da lista de parâmetros sem criar uma matriz totalmente nova.
Páginas que eu já vi:
- http://mywiki.wooledge.org/ComplexOptionParsing#Rearranging_arguments
- http://mywiki.wooledge.org/BashFAQ/035
- Usando getopts no script shell do bash para obter opções de linha de comando longas e curtas
- http://wiki.bash-hackers.org/scripting/posparams
- http://wiki.bash-hackers.org/howto/getopts_tutorial
- caso de argumento do bash para args em $ @
- Qual é a maneira canônica de implementar opções independentes de pedidos em scripts bash?
- Como lidar com opções em um script de shell?
Respostas:
POSIXIAMENTE, a análise de opções deve parar no
--
primeiro argumento de não-opção (ou não-opção-argumento), o que ocorrer primeiro. Então emEssa é, pelo
file1
, entãocp
recursivamente deve copiar todosfile1
,-t
,/mybackup
efile2
para o-f
diretório.getopt(3)
No entanto, o GNU (que o GNUcp
usa para analisar opções (e aqui você está usando o GNUcp
desde que está usando a-t
opção específica do GNU )), a menos que a$POSIXLY_CORRECT
variável de ambiente esteja definida, aceita opções após argumentos. Portanto, é realmente equivalente à análise do estilo de opção POSIX:O
getopts
shell embutido, mesmo no shell GNU (bash
), lida apenas com o estilo POSIX. Também não suporta opções longas ou opções com argumentos opcionais.Se você deseja analisar as opções da mesma maneira que o GNU
cp
, precisará usar agetopt(3)
API do GNU . Para isso, no Linux, você pode usar ogetopt
utilitário aprimorado deutil-linux
( essa versão aprimorada dogetopt
comando também foi portada para alguns outros Unices como o FreeBSD ).Isso
getopt
reorganizará as opções de maneira canônica, permitindo que você as analise simplesmente com umwhile/case
loop.Você costuma usá-lo como:
Observe também que isso
getopt
analisará as opções da mesma maneira que o GNUcp
. Em particular, ele suporta as opções longas (e sua inserção abreviada) e honra as$POSIXLY_CORRECT
variáveis de ambiente (que quando configuradas desativam o suporte a opções após argumentos) da mesma forma que o GNUcp
.Observe que usar o gdb e imprimir os argumentos
getopt_long()
recebidos pode ajudar a criar os parâmetros paragetopt(1)
:Então você pode usar
getopt
como:Lembre-se de que
cp
a lista de opções suportadas do GNU pode mudar de uma versão para a próxima e quegetopt
não poderá verificar se você passa um valor legal para a--sparse
opção, por exemplo.fonte
while [ "$#" -gt 0 ]
apenas iowhile (($#))
? É apenas para evitar um basismo?(($#))
é mais um kshismo .Portanto, toda vez que
getopts
processa um argumento, ele não espera que defina a variável shell$OPTIND
como o próximo número na lista de argumentos que deve processar e retorne diferente de 0. Se$OPTIND
for definido com o valor 1,getopts
será especificado pelo POSIX para aceitar uma variável. nova lista de argumentos. Portanto, isso apenas observa ogetopts
retorno, salva incrementos de um contador de mais$OPTIND
por qualquer retorno com falha, muda os argumentos com falha e redefine$OPTIND
todas as tentativas com falha. Você pode usá-lo comoopts "$@"
- embora queira personalizar ocase
loop ou salvá-lo em uma variável e alterar essa seção paraeval $case
.Durante a execução, ele define
$args
todos os argumentos quegetopts
não trataram ... então ...RESULTADO
Isso funciona em
bash
,dash
,zsh
,ksh93
,mksh
... bem, eu parar de tentar naquele ponto. Em todas as conchas também tem$[Rtf]flag
e$targ
. O ponto é que todos os números dos argumentos quegetopts
não desejavam processar permaneceram.Alterar o estilo das opções também não fez diferença. Funcionou como
-Rf -t/mybackup
ou-R -f -t /mybackup
. Funcionou no meio da lista, no final da lista ou no início da lista ...Ainda assim, a melhor maneira é colocar um
--
final de opções na sua lista de argumentos e, em seguida, executarshift "$(($OPTIND-1))"
no final de umagetopts
execução de processamento. Dessa forma, você remove todos os parâmetros processados e mantém o final da lista de argumentos.Uma coisa que gosto de fazer é traduzir opções longas para curtas - e faço isso de maneira muito semelhante, e é por isso que essa resposta veio com facilidade - antes de executar
getopts
.fonte
cp -t --file foo
oucp -- --file foo
) e não lidaria com as opções digitadas abreviadas (--fi
...) ou com a--target=/dest
sintaxe.getopts
coisa por uma função muito mais simples.opts()
tem alguma influência em sua própria resposta, poisopts()
funciona em um único loop - tocando cada argumento apenas uma vez - e o faz de maneira confiável (o mais próximo que posso dizer) , de maneira portável e sem um único subconjunto.-R
,-t /mybackup
,-f
). Vou manter meu voto negativo por enquanto, ainda está ofuscado, você está usando$a
não inicializado eargs= opts...
é provável que fiqueargs
desabilitado (ou definido como era antes) depois deopts
retornos em muitas conchas (incluindo o bash).bash
- eu odeio isso. Na minha opinião, se a função é uma função shell atual que deve ser mantida. Estou abordando essas questões - já que a função atual, com mais testes, poderia ser robusta para lidar com todos os casos e até sinalizar argumentos com sua opção anterior, mas discordo que ela esteja ofuscada . Como está escrito, é o meio mais simples e mais direto de realizar sua tarefa em que consigo pensar.test
ingshift
ing faz pouco sentido quando, em todos osshift
casos com falha , você deveriareturn
. Não se destina a fazer o contrário.