Wie verwende ich getopt in der Bash-Befehlszeile mit nur langen Optionen?

Wie verwende ich getopt in der Bash-Befehlszeile mit nur langen Optionen?

Es gibt einen getoptBefehl in der Bash-Befehlszeile. getoptkann 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-keydie Option jedoch nicht richtig. Wie kann ich also getoptden Befehl mitnurlange Optionen? Oder ist das unmöglich oder ist das nur ein Fehler des getoptBefehls?

Antwort1

getoptist 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 -ooder --optionsOption gefunden wird, wird der erste Parameter des zweiten Teils als kurze Optionszeichenfolge verwendet.

Das passiert in Ihrem Test: getopt -l long-key -- --long-key foobehandelt --long-keyals Liste der Optionen -egklnoyund fooals 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 getoptsintegrierte 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 indas 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 getoptzu 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 --flagin der Argumentliste löst also aus flag=1. Dies wird nicht zusammengesetzt - --flag --flag --flages 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 evaloben 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 ... inFeld 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

verwandte Informationen