Se for portável para uma gama de Unices, você terá que se ater ao POSIX sh. E AFAIU lá você simplesmente não tem escolha a não ser manipular argumento manualmente.
Atualmente estou escrevendo um script Bash que possui os seguintes requisitos:
Eu sei que getopts
seria o caminho preferido em termos de portabilidade, mas a AFAIK não suporta opções longas.
getopt
suporta opções longas, mas o BashGuide recomenda veementemente:
Never use getopt(1). getopt cannot handle empty arguments strings, or arguments with embedded whitespace. Please forget that it ever existed.
Então, ainda há a opção de análise manual. Isso é propenso a erros, produz bastante código clichê, e eu preciso lidar com erros por mim mesmo (eu acho que getopt(s)
faz o tratamento de erros por conta própria).
Então, qual seria a escolha preferida neste caso?
getopt
vs getopts
parece ser uma questão religiosa. Quanto aos argumentos contra getopt
na FAQ da Bash :
" getopt
não suporta strings de argumentos vazios" parece referir-se a um problema conhecido com argumentos optional , que parece que getopts
não suporta de todo (pelo menos da leitura help getopts
para o Bash 4.2.24). De man getopt
:
getopt(3) can parse long options with optional arguments that are given an empty optional argument (but can not do this for short options). This getopt(1) treats optional arguments that are empty as if they were not present.
Eu não sei onde o " getopt
não pode lidar [...] com argumentos com espaço em branco embutido" vem, mas vamos testá-lo:
test.sh:
#!/usr/bin/env bash
set -o errexit -o noclobber -o nounset -o pipefail
params="$(getopt -o ab:c -l alpha,bravo:,charlie --name "$0" -- "$@")"
eval set -- "$params"
while true
do
case "$1" in
-a|--alpha)
echo alpha
shift
;;
-b|--bravo)
echo "bravo=$2"
shift 2
;;
-c|--charlie)
echo charlie
shift
;;
--)
shift
break
;;
*)
echo "Not implemented: $1" >&2
exit 1
;;
esac
done
executar:
$ ./test.sh -
$ ./test.sh -acb ' whitespace FTW '
alpha
charlie
bravo= whitespace FTW
$ ./test.sh -ab '' -c
alpha
bravo=
charlie
$ ./test.sh --alpha --bravo ' whitespace FTW ' --charlie
alpha
bravo= whitespace FTW
charlie
Parece checar e acasalar comigo, mas tenho certeza que alguém vai mostrar como eu entendi completamente a frase. Claro que a questão da portabilidade ainda permanece; você terá que decidir quanto tempo vale a pena investir em plataformas com um Bash antigo ou não disponível. Minha dica é usar o YAGNI e KISS - Desenvolva-se apenas para as plataformas específicas que você sabe que serão usadas. A portabilidade do código shell geralmente vai para 100%, conforme o tempo de desenvolvimento vai para o infinito.
Há este getopts_long escrito como uma função de shell POSIX que você pode inserir dentro do seu script.
Observe que o Linux getopt
(de util-linux
) funciona corretamente quando não está no modo tradicional e oferece suporte a opções longas, mas provavelmente não é uma opção para você se precisar ser portátil para outros Unices.
As versões recentes de ksh93 ( getopts
) e zsh ( zparseopts
) têm suporte interno para analisar opções longas que podem ser uma opção para você, pois estão disponíveis para a maioria dos Unices (embora não sejam instaladas por padrão) .
Outra opção seria usar perl
e seu módulo Getopt::Long
, que devem estar disponíveis na maioria dos Unices hoje, seja escrevendo o script inteiro em perl
ou apenas chamar perl apenas para analisar a opção e o feed a informação extraída para o shell. Algo como:
parsed_ops=$(
perl -MGetopt::Long -le '
@options = (
"foo=s", "bar", "neg!"
);
Getopt::Long::Configure "bundling";
$q="'\''";
GetOptions(@options) or exit 1;
for (map /(\w+)/, @options) {
eval "\$o=\$opt_$_";
$o =~ s/$q/$q\$q$q/g;
print "opt_$_=$q$o$q"
}' -- "$@"
) || exit
eval "$parsed_ops"
# and then use $opt_foo, $opt_bar...
Veja perldoc Getopt::Long
para o que ele pode fazer e como ele difere de outros analisadores de opções.
Todas as discussões sobre esse assunto destacam a opção de escrever o código de análise manualmente - só assim você pode ter certeza sobre a funcionalidade e a portabilidade. Eu aconselho você a não escrever código que possa ser gerado e re-gerado por geradores de código de código aberto fáceis de usar. Use o Argbash , que foi criado para fornecer a resposta definitiva para o seu problema. É um gerador de códigos bem documentado disponível como aplicativo de linha de comando , on-line ou como um Imagem do compartimento .
Eu aconselho contra bibliotecas bash, algumas delas usam getopt
(o que torna bastante não-portável) e é difícil empacotar um gigantesco e ilegível blob shell com seu script.
Você pode usar getopt
em sistemas que o suportam e usar um fallback para sistemas que não o fazem.
Por exemplo pure-getopt
é implementado no Bash puro para substituir o GNU getopt
.