
Es gibt einen getopt
Befehl in der Bash-Befehlszeile. getopt
kann mit kurzen Optionen (wie getopt -o axby "$@"
) verwendet werden und kann sowohl mit kurzen als auch mit langen Optionen (wie getopt -o axby -l long-key -- "$@"
) verwendet werden, aber jetzt brauche ichnurlange Optionen (d. h. kurze Optionen gibt es überhaupt nicht), der Befehl getopt -l long-key -- "$@"
analysiert --long-key
die Option jedoch nicht richtig. Wie kann ich also getopt
den Befehl mitnurlange Optionen? Oder ist das unmöglich oder ist das nur ein Fehler des getopt
Befehls?
Antwort1
getopt
ist vollkommen in Ordnung, keine kurzen Optionen zu haben. Aber Sie müssen ihm sagen, dass Sie keine kurzen Optionen haben. Es ist eine Eigenart in der Syntax – aus dem Handbuch:
Wenn im ersten Teil keine
-o
oder--options
Option gefunden wird, wird der erste Parameter des zweiten Teils als kurze Optionszeichenfolge verwendet.
Das passiert in Ihrem Test: getopt -l long-key -- --long-key foo
behandelt --long-key
als Liste der Optionen -egklnoy
und foo
als einziges Argument. Verwenden Sie
getopt -o '' -l long-key -- "$@"
z.B
$ getopt -l long-key -o '' -- --long-key foo
--long-key -- 'foo'
$ getopt -l long-key -o '' -- --long-key --not-recognized -n foo
getopt: unrecognized option '--not-recognized'
getopt: invalid option -- 'n'
--long-key -- 'foo'
Antwort2
Keine Ahnung getopt
, aber das getopts
integrierte Feature kann nur zur Verarbeitung langer Optionen wie dieser verwendet werden:
while getopts :-: o
do case "$o$OPTARG" in
(-longopt1) process ;;
(-longopt2) process ;;
esac; done
Natürlich funktioniert das so nicht, wenn die langen Optionen Argumente haben sollen. Es ist zwar möglich, aber wie ich bei der Arbeit daran gelernt habe. Als ich es hier ursprünglich eingefügt habe, habe ich festgestellt, dass es für lange Optionen nicht sehr nützlich ist. In diesem Fall hat es meine case
(match)
Felder nur um ein einziges, vorhersehbares Zeichen verkürzt. Was ich jetzt weiß, ist, dass es für kurze Optionen hervorragend ist – es ist am nützlichsten, wenn es eine Schleife über eine Zeichenfolge unbekannter Länge durchläuft und einzelne Bytes entsprechend ihrer Optionszeichenfolge auswählt. Aber wenn die OptionIstfor var do case $var in
das Argument, mit einer Kombination, die man machen könnte, kann man nicht viel anfangen . Ich denke, es ist besser, es einfach zu halten.
Ich vermute, dass das Gleiche für gilt , aber ich weiß nicht genug darüber, um das mit Sicherheit sagen zu können. Anhand des folgenden Argument-Arrays werde ich meinen eigenen kleinen Argument-Parser demonstrieren – der hauptsächlich auf der Auswertungs-/Zuweisungsbeziehung basiert, die ich für und getopt
zu schätzen gelernt habe .alias
$((shell=math))
set -- this is ignored by default --lopt1 -s 'some '\''
args' here --ignored and these are ignored \
--alsoignored andthis --lopt2 'and
some "`more' --lopt1 and just a few more
Das ist die Argumentzeichenfolge, mit der ich arbeiten werde. Jetzt:
aopts() { env - sh -s -- "$@"
} <<OPTCASE 3<<\OPTSCRIPT
acase() case "\$a" in $(fmt='
(%s) f=%s; aset "?$(($f)):";;\n'
for a do case "$a" in (--) break;;
(--*[!_[:alnum:]]*) continue;;
(--*) printf "$fmt" "$a" "${a#--}";;
esac;done;printf "$fmt" '--*' ignored)
(*) aset "" "\$a";;esac
shift "$((SHIFT$$))"; f=ignored; exec <&3
OPTCASE
aset() { alias "$f=$(($f${1:-=$(($f))+}1))"
[ -n "${2+?}" ] && alias "${f}_$(($f))=$2"; }
for a do acase; done; alias
#END
OPTSCRIPT
Dadurch wird das Argument-Array auf eine von zwei Arten verarbeitet, je nachdem, ob Sie ihm einen oder zwei durch das Trennzeichen getrennte Argumentsätze übergeben --
. In beiden Fällen gilt dies für Verarbeitungssequenzen des Argument-Arrays.
Wenn Sie es so nennen:
: $((SHIFT$$=3)); aopts --lopt1 --lopt2 -- "$@"
Als Erstes muss die acase()
Funktion so geschrieben werden, dass sie wie folgt aussieht:
acase() case "$a" in
(--lopt1) f=lopt1; aset "?$(($f)):";;
(--lopt2) f=lopt2; aset "?$(($f)):";;
(--*) f=ignored; aset "?$(($f)):";;
(*) aset "" "$a";;esac
Und neben shift 3
. Die Befehlssubstitution in der acase()
Funktionsdefinition wird ausgewertet, wenn die aufrufende Shell die Eingabedokumente der Funktion erstellt, acase()
wird aber in der aufrufenden Shell nie aufgerufen oder definiert. Sie wird jedoch natürlich in der Subshell aufgerufen, und auf diese Weise können Sie die gewünschten Optionen dynamisch in der Befehlszeile angeben.
Wenn Sie ihm ein Array ohne Trennzeichen übergeben, wird es einfach acase()
mit Übereinstimmungen für alle Argumente gefüllt, die mit der Zeichenfolge beginnen --
.
Die Funktion führt praktisch ihre gesamte Verarbeitung in der Subshell durch – sie speichert iterativ jeden Wert des Arguments in Aliasnamen, denen assoziative Namen zugewiesen wurden. Wenn sie fertig ist, druckt sie jeden Wert aus, mit dem sie gespeichert hat alias
– was POSIX-spezifiziert ist, um alle gespeicherten Werte in Anführungszeichen so auszudrucken, dass ihre Werte erneut in die Shell eingegeben werden können. Wenn ich das also mache …
aopts --lopt1 --lopt2 -- "$@"
Die Ausgabe sieht folgendermaßen aus:
...ignored...
lopt1='8'
lopt1_1='-s'
lopt1_2='some '\'' args'
lopt1_3='here'
lopt1_4='and'
lopt1_5='just'
lopt1_6='a'
lopt1_7='few'
lopt1_8='more'
lopt2='1'
lopt2_1='and
some "`more'
Während es die Argumentenliste durchläuft, prüft es den Case-Block auf eine Übereinstimmung. Wenn es dort eine Übereinstimmung findet, wirft es ein Flag - f=optname
. Bis es wieder eine gültige Option findet, fügt es jedes nachfolgende Argument einem Array hinzu, das es basierend auf dem aktuellen Flag erstellt. Wenn dieselbe Option mehrmals angegeben wird, werden die Ergebnisse zusammengesetzt und nicht überschrieben. Alles, was nicht im Case steht – oder alle Argumente, die ignorierten Optionen folgen – werden einemignoriert-Array.
Die Ausgabe wird von der Shell automatisch für die Shell-Eingabe gesichert, und zwar:
eval "$(: $((SHIFT$$=3));aopts --lopt1 --lopt2 -- "$@")"
...sollte absolut sicher sein. Sollte es aus irgendeinem Grundist nichtsicher, dann sollten Sie wahrscheinlich einen Fehlerbericht bei Ihrem Shell-Betreuer einreichen.
Es weist zwei Arten von Aliaswerten für jede Übereinstimmung zu. Zuerst setzt es ein Flag - dies geschieht unabhängig davon, ob eine Option nicht übereinstimmenden Argumenten vorangeht oder nicht. Jedes Vorkommen von --flag
in der Argumentliste löst also aus flag=1
. Dies wird nicht zusammengesetzt - --flag --flag --flag
es erhält nur flag=1
. Dieser WerttutInkrement jedoch – für alle Argumente, die darauf folgen könnten. Es kann als Indexschlüssel verwendet werden. Nachdem ich das eval
oben genannte getan habe, kann ich Folgendes tun:
printf %s\\n "$lopt1" "$lopt2"
...zu bekommen...
8
1
Und so:
for o in lopt1 lopt2
do list= i=0; echo "$o = $(($o))"
while [ "$((i=$i+1))" -le "$(($o))" ]
do list="$list $o $i \"\${${o}_$i}\" "
done; eval "printf '%s[%02d] = %s\n' $list"; done
AUSGABE
lopt1 = 8
lopt1[01] = -s
lopt1[02] = some ' args
lopt1[03] = here
lopt1[04] = and
lopt1[05] = just
lopt1[06] = a
lopt1[07] = few
lopt1[08] = more
lopt2 = 1
lopt2[01] = and
some "`more
Und für Argumente, die nicht übereinstimmten, würde ich ersetzenignoriertin das obige for ... in
Feld ein, um Folgendes zu erhalten:
ignored = 10
ignored[01] = this
ignored[02] = is
ignored[03] = ignored
ignored[04] = by
ignored[05] = default
ignored[06] = and
ignored[07] = these
ignored[08] = are
ignored[09] = ignored
ignored[10] = andthis