Outra solução sem getopt [s], POSIX, antigo estilo Unix
Semelhante à solução que Bruno Bronosky publicou aqui é uma sem o uso de getopt(s)
.
O principal diferencial da minha solução é que ela permite concatenar opções da mesma forma que tar -xzf foo.tar.gz
é igual a tar -x -z -f foo.tar.gz
. E, assim como em etc. tar
, ps
o hífen principal é opcional para um bloco de opções curtas (mas isso pode ser alterado facilmente). Opções longas também são suportadas (mas quando um bloco começa com um, são necessários dois hífens principais).
Código com opções de exemplo
#!/bin/sh
echo
echo "POSIX-compliant getopt(s)-free old-style-supporting option parser from phk@[se.unix]"
echo
print_usage() {
echo "Usage:
$0 {a|b|c} [ARG...]
Options:
--aaa-0-args
-a
Option without arguments.
--bbb-1-args ARG
-b ARG
Option with one argument.
--ccc-2-args ARG1 ARG2
-c ARG1 ARG2
Option with two arguments.
" >&2
}
if [ $# -le 0 ]; then
print_usage
exit 1
fi
opt=
while :; do
if [ $# -le 0 ]; then
# no parameters remaining -> end option parsing
break
elif [ ! "$opt" ]; then
# we are at the beginning of a fresh block
# remove optional leading hyphen and strip trailing whitespaces
opt=$(echo "$1" | sed 's/^-\?\([a-zA-Z0-9\?-]*\)/\1/')
fi
# get the first character -> check whether long option
first_chr=$(echo "$opt" | awk '{print substr($1, 1, 1)}')
[ "$first_chr" = - ] && long_option=T || long_option=F
# note to write the options here with a leading hyphen less
# also do not forget to end short options with a star
case $opt in
-)
# end of options
shift
break
;;
a*|-aaa-0-args)
echo "Option AAA activated!"
;;
b*|-bbb-1-args)
if [ "$2" ]; then
echo "Option BBB with argument '$2' activated!"
shift
else
echo "BBB parameters incomplete!" >&2
print_usage
exit 1
fi
;;
c*|-ccc-2-args)
if [ "$2" ] && [ "$3" ]; then
echo "Option CCC with arguments '$2' and '$3' activated!"
shift 2
else
echo "CCC parameters incomplete!" >&2
print_usage
exit 1
fi
;;
h*|\?*|-help)
print_usage
exit 0
;;
*)
if [ "$long_option" = T ]; then
opt=$(echo "$opt" | awk '{print substr($1, 2)}')
else
opt=$first_chr
fi
printf 'Error: Unknown option: "%s"\n' "$opt" >&2
print_usage
exit 1
;;
esac
if [ "$long_option" = T ]; then
# if we had a long option then we are going to get a new block next
shift
opt=
else
# if we had a short option then just move to the next character
opt=$(echo "$opt" | awk '{print substr($1, 2)}')
# if block is now empty then shift to the next one
[ "$opt" ] || shift
fi
done
echo "Doing something..."
exit 0
Para o exemplo de uso, consulte os exemplos abaixo.
Posição das opções com argumentos
Pelo que vale a pena, as opções com argumentos não são as últimas (somente as opções longas precisam ser). Portanto, enquanto que, por exemplo, em tar
(pelo menos em algumas implementações) as f
opções precisam ser as últimas, porque o nome do arquivo segue ( tar xzf bar.tar.gz
funciona, mas tar xfz bar.tar.gz
não funciona ), esse não é o caso aqui (veja os exemplos posteriores).
Várias opções com argumentos
Como outro bônus, os parâmetros das opções são consumidos na ordem das opções pelos parâmetros com as opções necessárias. Basta olhar para a saída do meu script aqui com a linha de comando abc X Y Z
(ou -abc X Y Z
):
Option AAA activated!
Option BBB with argument 'X' activated!
Option CCC with arguments 'Y' and 'Z' activated!
Opções longas concatenadas também
Também é possível ter opções longas no bloco de opções, uma vez que elas ocorrem pela última vez no bloco. Portanto, as seguintes linhas de comando são todas equivalentes (incluindo a ordem em que as opções e seus argumentos estão sendo processados):
-cba Z Y X
cba Z Y X
-cb-aaa-0-args Z Y X
-c-bbb-1-args Z Y X -a
--ccc-2-args Z Y -ba X
c Z Y b X a
-c Z Y -b X -a
--ccc-2-args Z Y --bbb-1-args X --aaa-0-args
Tudo isso leva a:
Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...
Não nesta solução
Argumentos opcionais
Opções com argumentos opcionais devem ser possíveis com um pouco de trabalho, por exemplo, observando se existe um bloco sem hífen; o usuário precisaria colocar um hífen na frente de cada bloco após um bloco com um parâmetro com um parâmetro opcional. Talvez isso seja muito complicado para se comunicar com o usuário; portanto, é necessário apenas um hífen principal neste caso.
As coisas ficam ainda mais complicadas com vários parâmetros possíveis. Eu desaconselharia a fazer as opções tentarem ser inteligentes, determinando se o argumento pode ser a favor ou não (por exemplo, com uma opção aceita apenas um número como argumento opcional), pois isso pode ser interrompido no futuro.
Pessoalmente, sou a favor de opções adicionais em vez de argumentos opcionais.
Argumentos de opção introduzidos com um sinal de igual
Assim como nos argumentos opcionais, eu não sou fã disso (BTW, existe um tópico para discutir os prós / contras de diferentes estilos de parâmetros?), Mas se você quiser isso, provavelmente poderá implementá-lo por conta própria, como feito em http: // mywiki.wooledge.org/BashFAQ/035#Manual_loop com um--long-with-arg=?*
declaração de caso e, em seguida, retira o sinal de igual (este é o site que diz que fazer concatenação de parâmetros é possível com algum esforço, mas "o deixou como um exercício para o leitor "o que me fez acreditar na palavra deles, mas comecei do zero).
Outras notas
Compatível com POSIX, funciona mesmo em configurações antigas do Busybox com as quais tive que lidar (com cut
, por exemplo , head
e getopts
ausente).
zparseopts -D -E -M -- d=debug -debug=d
E ter ambos-d
e--debug
na$debug
matrizecho $+debug[1]
retornará 0 ou 1 se um deles for usado. Ref: zsh.org/mla/users/2011/msg00350.html