Eine andere Lösung ohne getopt[s], POSIX, alter Unix-Stil
Ähnlich wie die von Bruno Bronosky gepostete Lösung dies hier ist eine ohne die Verwendung von getopt(s)
.
Das Hauptunterscheidungsmerkmal meiner Lösung ist, dass sie es erlaubt, Optionen miteinander zu verketten, genau wie tar -xzf foo.tar.gz
ist gleich tar -x -z -f foo.tar.gz
. Und genau wie in tar
, ps
usw. ist der führende Bindestrich für einen Block kurzer Optionen optional (kann aber leicht geändert werden). Lange Optionen werden ebenfalls unterstützt (wenn ein Block mit einer Option beginnt, sind jedoch zwei führende Bindestriche erforderlich).
Code mit Beispieloptionen
#!/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
Beispiele für die Verwendung finden Sie weiter unten.
Position der Optionen mit Argumenten
Dort müssen die Optionen mit Argumenten nicht die letzten sein (nur lange Optionen müssen es sein). Während also z.B. in tar
(zumindest in einigen Implementierungen) die f
muss an letzter Stelle stehen, da der Dateiname folgt ( tar xzf bar.tar.gz
funktioniert aber tar xfz bar.tar.gz
nicht), ist dies hier nicht der Fall (siehe die späteren Beispiele).
Mehrere Optionen mit Argumenten
Als weiterer Bonus werden die Optionsparameter in der Reihenfolge der Optionen von den Parametern mit den erforderlichen Optionen verbraucht. Schauen Sie sich einfach die Ausgabe meines Skripts mit der Befehlszeile an abc X Y Z
(oder -abc X Y Z
):
Option AAA activated!
Option BBB with argument 'X' activated!
Option CCC with arguments 'Y' and 'Z' activated!
Auch lange Optionen werden verkettet
Sie können auch lange Optionen in Optionsblöcken verwenden, sofern sie am Ende des Blocks stehen. Die folgenden Befehlszeilen sind also alle gleichwertig (einschließlich der Reihenfolge, in der die Optionen und ihre Argumente verarbeitet werden):
-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
All dies führt zu:
Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...
Nicht in dieser Lösung
Optionale Argumente
Optionen mit optionalen Argumenten sollten mit ein wenig Arbeit möglich sein, z.B. indem man nach vorne schaut, ob es einen Block ohne Bindestrich gibt; der Benutzer müsste dann einen Bindestrich vor jeden Block setzen, der auf einen Block mit einem Parameter folgt, der einen optionalen Parameter hat. Vielleicht ist dies zu kompliziert, um es dem Benutzer mitzuteilen, so dass es besser ist, in diesem Fall nur einen führenden Bindestrich zu verlangen.
Noch komplizierter wird es bei mehreren möglichen Parametern. Ich würde davon abraten, die Optionen so zu gestalten, dass sie versuchen, intelligent zu sein, indem sie feststellen, ob ein Argument dafür geeignet ist oder nicht (z.B. mit einer Option, die nur eine Zahl als optionales Argument annimmt), da dies in der Zukunft scheitern könnte.
Ich persönlich bevorzuge zusätzliche Optionen anstelle von optionalen Argumenten.
Optionsargumente, die mit einem Gleichheitszeichen eingeleitet werden
Genau wie bei optionalen Argumenten bin ich kein Fan davon (BTW, gibt es einen Thread für die Diskussion der Vor- und Nachteile verschiedener Parameterstile?), aber wenn Sie das wollen, könnten Sie es wahrscheinlich selbst implementieren, so wie es bei http://mywiki.wooledge.org/BashFAQ/035#Manual_loop mit einer --long-with-arg=?*
case-Anweisung und dann das Entfernen des Gleichheitszeichens (dies ist übrigens die Seite, die sagt, dass die Verkettung von Parametern mit einigem Aufwand möglich ist, aber "es als Übung für den Leser belässt", was mich dazu brachte, sie beim Wort zu nehmen, aber ich begann von vorne).
Sonstige Anmerkungen
POSIX-kompatibel, funktioniert sogar auf alten Busybox-Systemen, mit denen ich zu tun hatte (z.B. mit cut
, head
y getopts
fehlt).
1 Stimmen
Für zsh-Benutzer gibt es ein großartiges builtin namens zparseopts, das das kann:
zparseopts -D -E -M -- d=debug -debug=d
Und haben beide-d
y--debug
im$debug
Arrayecho $+debug[1]
gibt 0 oder 1 zurück, wenn einer dieser Werte verwendet wird. Ref: zsh.org/mla/users/2011/msg00350.html2 Stimmen
Ein wirklich gutes Tutorial: linuxcommand.org/lc3_wss0120.php . Besonders gut gefällt mir das Beispiel "Befehlszeilenoptionen".
0 Stimmen
Ich habe ein Skript erstellt, das dies für Sie erledigt, es heißt - github.com/unfor19/bargs
2 Stimmen
Siehe auch Einem Bash-Skript die Möglichkeit geben, Flags zu akzeptieren, wie ein Befehl? für einen ausgeklügelten Ad-hoc-Parser für lange und kurze Optionen. Es wird nicht versucht, Optionsargumente zu behandeln, die an kurze Optionen angehängt sind, noch lange Optionen mit
=
die den Optionsnamen vom Optionswert trennt (in beiden Fällen wird einfach angenommen, dass der Optionswert im nächsten Argument steht). Es behandelt auch nicht die Clusterung von Kurzoptionen - die Frage brauchte das nicht.0 Stimmen
Diese tolle Anleitung von Baeldung zeigt 4 Möglichkeiten zur Verarbeitung von Kommandozeilenargumenten in der Bash, darunter: 1) Positionsbezogene Parameter
$1
,$2
, usw., 2) Flaggen mitgetopts
y${OPTARG}
3) Schleifenbildung über alle Parameter ($@
), und 4) Schleifenbildung über alle Parameter mit$#
,$1
und dieshift
Betreiber.0 Stimmen
Prüfen Sie die Lösung mit Bash Space-Separated: bigdata-etl.com/bash-parse-input-arguments-funtions-parameters