Wie verwende ich getopt in der bash Kommandozeile mit nur langen Optionen?

12

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 ich nur lange Optionen (dh kurze Optionen gibt es überhaupt nicht), der Befehl getopt -l long-key -- "$@"jedoch nicht --long-keyOption richtig analysieren . Wie kann ich einen getoptBefehl nur mit langen Optionen verwenden? Oder ist es unmöglich oder ist es nur ein Fehler des getoptBefehls?

Sieger
quelle
Sie taggen für das interne getopts, aber Sie verwenden den /usr/bin/getoptBefehl.
Anthon
@Anthon Leider habe ich das falsche Tag verwendet, aber ich habe nicht genug Reputationen, um ein weiteres Tag hinzuzufügen, das 300 Reputationen benötigt. Ich habe jedoch gerade das falsche Tag gelöscht.
Victor

Antworten:

14

getoptist vollkommen in Ordnung, wenn man keine kurzen Optionen hat. Aber Sie müssen ihm sagen, dass Sie keine kurzen Möglichkeiten haben. Es ist eine Kuriosität in der Syntax - aus dem Handbuch:

Wird im ersten Teil keine -ooder --optionsOption gefunden, wird der erste Parameter des zweiten Teils als kurze Optionszeichenfolge verwendet.

Das ist, was in Ihrem Test passiert: getopt -l long-key -- --long-key fooBehandelt --long-keyals die Liste der Optionen -egklnoyund fooals das einzige Argument. Verwenden

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'
Gilles 'SO - hör auf böse zu sein'
quelle
Hat sich das OP mit Ihrer Antwort vermischt getoptsund getoptinfiziert? Sie beginnen mit dem Kommentieren getoptsund erwähnen dann nur noch getopt.
Anthon
@Anthon Alle meine Antworten beziehen sich auf das getoptProgramm von GNU coreutils, worum es in der Frage geht. Ich habe den besagten Text korrigiert getopts. Vielen Dank. getoptsmacht nicht einmal lange Optionen, also würde nichts davon zutreffen getopts.
Gilles 'SO - hör auf, böse zu sein'
Das OP hatte ursprünglich den getoptsTag. Ich wollte deine Antwort nicht ändern, weil du normalerweise viel besser weißt als ich, worüber du schreibst :-)
Anthon
Ich habe fast eine Stunde verloren, um das herauszufinden. Du hast mir gerade ein paar vergebliche Kaffee-Schlucke erspart. Danke ... lasst uns diesen Kaffee jetzt besser nutzen. ☕️
dmmd
1

Weiß nicht über , getoptaber die getoptsbuiltin kann nur lange Optionen wie das zu handhaben verwendet werden:

while getopts :-: o
do  case "$o$OPTARG" in
(-longopt1) process ;;
(-longopt2) process ;;
esac; done

Natürlich funktioniert das so wie es ist nicht, wenn die long-Optionen Argumente haben sollen. Es kann aber gemacht werden, wie ich gelernt habe, daran zu arbeiten. Während ich es anfangs hier aufgenommen habe, wurde mir klar, dass es für Long-Optionen nicht sehr nützlich ist. In diesem Fall verkürzte es meine case (match)Felder nur um ein einziges vorhersehbares Zeichen. Was ich jetzt weiß, ist, dass es sich hervorragend für kurze Optionen eignet - es ist am nützlichsten, wenn es eine Zeichenkette unbekannter Länge durchläuft und einzelne Bytes gemäß ihrer Optionszeichenkette auswählt. Aber wenn die Option ist die arg, es gibt wenig , was man mit einer tust for var do case $var inKombination , die es tun könnte. Ich denke, es ist besser, es einfach zu halten.

Ich vermute, dasselbe gilt, getoptaber ich weiß nicht genug darüber, um es mit Sicherheit zu sagen. In Anbetracht der folgenden arg Array, werde ich meine eigene kleine arg Parser zeigen - die in erster Linie auf der evalation / Zuordnung Beziehung hängt Ich bin gekommen , um zu schätzen , aliasund $((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 Arg-Saite, 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

Dabei wird das arg-Array auf zwei verschiedene Arten verarbeitet, je nachdem, ob Sie ein oder zwei durch das --Trennzeichen getrennte Argumente übergeben . In beiden Fällen gilt dies für Verarbeitungssequenzen für das arg-Array.

Wenn du es so nennst:

: $((SHIFT$$=3)); aopts --lopt1 --lopt2 -- "$@"

Seine erste acase()Aufgabe wird es sein, seine Funktion so zu schreiben , dass sie so 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 Befehlsersetzung in der acase()Funktionsdefinition wird ausgewertet, wenn die aufrufende Shell die Eingaben der Funktion in diesem Dokument erstellt, sie wird jedoch acase()niemals in der aufrufenden Shell aufgerufen oder definiert. Der Aufruf erfolgt jedoch in der Subshell. Auf diese Weise können Sie die gewünschten Optionen dynamisch in der Befehlszeile angeben.

Wenn Sie ein Array ohne Trennzeichen übergeben, werden nur acase()Übereinstimmungen für alle Argumente eingetragen, die mit der Zeichenfolge beginnen --.

Die Funktion führt praktisch die gesamte Verarbeitung in der Subshell aus. Dabei werden die einzelnen Werte des Args iterativ in Aliasnamen gespeichert, denen assoziative Namen zugewiesen sind. Wenn es durch ist, druckt es jeden Wert aus, mit aliasdem es gespeichert hat. Dies ist POSIX-spezifiziert, um alle gespeicherten Werte so zu drucken, dass ihre Werte wieder in die Shell eingegeben werden können. Also, wenn ich es tue ...

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'

Beim Durchlaufen der Argumentliste wird nach einer Übereinstimmung mit dem case-Block gesucht. Wenn es dort eine Übereinstimmung findet, wirft es eine Flagge - f=optname. Bis es wieder eine gültige Option findet, wird jedes nachfolgende Argument zu einem Array hinzugefügt, das es basierend auf dem aktuellen Flag erstellt. Wenn dieselbe Option mehrmals angegeben wird, werden die Ergebnisse zusammengesetzt und nicht überschrieben. Alles, was nicht der Fall ist - oder Argumente, die auf ignorierte Optionen folgen -, werden einem ignorierten Array zugewiesen .

Die Ausgabe wird von der Shell automatisch für die Shell-Eingabe gespeichert.

eval "$(: $((SHIFT$$=3));aopts --lopt1 --lopt2 -- "$@")"

... sollte absolut sicher sein. Wenn es aus irgendeinem Grund nicht sicher ist, sollten Sie wahrscheinlich einen Fehlerbericht bei Ihrem Shell-Betreuer einreichen.

Es werden zwei Arten von Aliaswerten für jede Übereinstimmung zugewiesen. Erstens wird ein Flag gesetzt - dies geschieht unabhängig davon, ob eine Option nicht übereinstimmenden Argumenten vorangeht oder nicht. Jedes Vorkommen von --flagin der Argumentliste wird also ausgelöst flag=1. Das verbindet nicht - wird --flag --flag --flagnur flag=1. Dieser Wert hat Zuwachs obwohl - für alle Argumente , die ihm 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"

...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 zu Argumenten, die nicht passen, würde ich im obigen Feld ignoriert ersetzen for ... in, um zu bekommen:

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
mikeserv
quelle