Une autre solution sans getopt [s], POSIX, ancien style Unix
Semblable à la solution que Bruno Bronosky a postée, celle-ci est sans utilisation getopt(s)
.
La principale caractéristique de différenciation de ma solution est qu'elle permet d'avoir des options concaténées ensemble comme tar -xzf foo.tar.gz
est égal à tar -x -z -f foo.tar.gz
. Et comme dans tar
,ps
etc., le trait d'union est facultatif pour un bloc d'options courtes (mais cela peut être changé facilement). Les options longues sont également prises en charge (mais lorsqu'un bloc commence par un puis deux tirets de tête sont requis).
Code avec des exemples d'options
#!/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
Pour l'exemple d'utilisation, veuillez consulter les exemples ci-dessous.
Position des options avec arguments
Pour ce que cela vaut, les options avec arguments ne sont pas les dernières (seules les options longues doivent l'être). Ainsi, par exemple dans tar
(au moins dans certaines implémentations), les f
options doivent être les dernières car le nom de fichier suit ( tar xzf bar.tar.gz
fonctionne mais tar xfz bar.tar.gz
ne fonctionne pas), ce n'est pas le cas ici (voir les exemples ultérieurs).
Plusieurs options avec arguments
Comme autre bonus, les paramètres d'options sont consommés dans l'ordre des options par les paramètres avec les options requises. Regardez simplement la sortie de mon script ici avec la ligne de commande 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!
Options longues concaténées également
Vous pouvez également avoir des options longues dans le bloc d'options étant donné qu'elles se produisent en dernier dans le bloc. Les lignes de commande suivantes sont donc toutes équivalentes (y compris l'ordre dans lequel les options et ses arguments sont traités):
-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
Tous ces éléments conduisent à:
Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...
Pas dans cette solution
Arguments facultatifs
Les options avec des arguments optionnels devraient être possibles avec un peu de travail, par exemple en regardant en avant s'il y a un bloc sans trait d'union; l'utilisateur aurait alors besoin de mettre un trait d'union devant chaque bloc suivant un bloc avec un paramètre ayant un paramètre optionnel. Peut-être que cela est trop compliqué pour communiquer à l'utilisateur, il vaut donc mieux avoir simplement un trait d'union dans ce cas.
Les choses deviennent encore plus compliquées avec plusieurs paramètres possibles. Je déconseille de faire des options en essayant d'être intelligent en déterminant si un argument pourrait être pour lui ou non (par exemple avec une option prend juste un nombre comme argument optionnel) car cela pourrait casser à l'avenir.
Personnellement, je préfère des options supplémentaires au lieu d'arguments facultatifs.
Arguments d'option introduits avec un signe égal
Tout comme avec les arguments facultatifs, je ne suis pas fan de cela (BTW, y a-t-il un fil pour discuter des avantages / inconvénients des différents styles de paramètres?) Mais si vous le souhaitez, vous pouvez probablement l'implémenter vous-même comme sur http: // mywiki.wooledge.org/BashFAQ/035#Manual_loop avec une --long-with-arg=?*
déclaration de cas, puis en supprimant le signe égal (c'est BTW le site qui dit que la concaténation de paramètres est possible avec un certain effort mais "l'a laissé" comme exercice pour le lecteur) "ce qui m'a fait les croire sur parole mais je suis parti de zéro).
Autres notes
Compatible POSIX, fonctionne même sur les anciennes configurations Busybox auxquelles j'ai dû faire face (par exemple cut
, head
et getopts
manquantes).
zparseopts -D -E -M -- d=debug -debug=d
Et avoir les deux-d
et--debug
dans le$debug
tableauecho $+debug[1]
retournera 0 ou 1 si l'un d'eux est utilisé. Ref: zsh.org/mla/users/2011/msg00350.html