Otra solución sin getopt [s], POSIX, antiguo estilo Unix
Similar a la solución que Bruno Bronosky publicó aquí es una sin el uso de getopt(s)
.
La principal característica diferenciadora de mi solución es que permite tener opciones concatenadas juntas como tar -xzf foo.tar.gz
es igual a tar -x -z -f foo.tar.gz
. Y al igual que en tar
,ps
etc. el guión principal es opcional para un bloque de opciones cortas (pero esto se puede cambiar fácilmente). Las opciones largas también son compatibles (pero cuando un bloque comienza con uno, se requieren dos guiones iniciales).
Código con opciones de ejemplo
#!/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 el uso de ejemplo, vea los ejemplos más abajo.
Posición de opciones con argumentos
Por lo que vale, las opciones con argumentos no son las últimas (solo las opciones largas deben ser). Entonces, por ejemplo, en tar
(al menos en algunas implementaciones) las f
opciones deben ser las últimas porque el nombre del archivo sigue ( tar xzf bar.tar.gz
funciona pero tar xfz bar.tar.gz
no funciona) este no es el caso aquí (ver los ejemplos posteriores).
Múltiples opciones con argumentos
Como otra ventaja, los parámetros de las opciones se consumen en el orden de las opciones por los parámetros con las opciones requeridas. Solo mira la salida de mi script aquí con la línea de comando abc X Y Z
(o -abc X Y Z
):
Option AAA activated!
Option BBB with argument 'X' activated!
Option CCC with arguments 'Y' and 'Z' activated!
Las opciones largas también se concatenan
Además, también puede tener opciones largas en el bloque de opciones dado que ocurren en último lugar en el bloque. Por lo tanto, las siguientes líneas de comando son todas equivalentes (incluido el orden en que se procesan las opciones y sus argumentos):
-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
Todos estos conducen a:
Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...
No en esta solución
Argumentos opcionales
Las opciones con argumentos opcionales deberían ser posibles con un poco de trabajo, por ejemplo, mirando hacia adelante si hay un bloque sin guión; el usuario necesitaría poner un guión delante de cada bloque después de un bloque con un parámetro que tenga un parámetro opcional. Tal vez esto sea demasiado complicado para comunicarse con el usuario, por lo que es mejor que solo requiera un guión principal en este caso.
Las cosas se vuelven aún más complicadas con múltiples parámetros posibles. Aconsejaría no hacer que las opciones intenten ser inteligentes al determinar si un argumento puede ser adecuado o no (por ejemplo, con una opción solo toma un número como argumento opcional) porque esto podría romperse en el futuro.
Personalmente, prefiero opciones adicionales en lugar de argumentos opcionales.
Opción argumentos introducidos con un signo igual
Al igual que con los argumentos opcionales, no soy fanático de esto (por cierto, ¿hay un hilo para discutir los pros / contras de los diferentes estilos de parámetros?) Pero si lo desea, probablemente podría implementarlo usted mismo como lo hizo en http: // mywiki.wooledge.org/BashFAQ/035#Manual_loop con un --long-with-arg=?*
enunciado de caso y luego quitando el signo igual (esto es, por cierto, el sitio que dice que es posible realizar la concatenación de parámetros con cierto esfuerzo, pero "lo dejó como un ejercicio para el lector "lo que me hizo tomarles la palabra pero empecé desde cero".
Otras notas
Compatible con POSIX, funciona incluso en configuraciones antiguas de Busybox con las que tuve que lidiar (por ejemplo cut
, head
y getopts
falta).
zparseopts -D -E -M -- d=debug -debug=d
Y tener ambos-d
y--debug
en la$debug
matrizecho $+debug[1]
devolverá 0 o 1 si se usa uno de esos. Ref: zsh.org/mla/users/2011/msg00350.html