Jak używać getopt w linii poleceń bash z tylko długimi opcjami?

W linii poleceń bash znajduje się polecenie getopt. getopt może być używany z krótkimi opcjami (takimi jak getopt -o axby "$@") i może być używany zarówno z krótkimi, jak i długimi opcjami (takimi jak getopt -o axby -l long-key -- "$@"), ale teraz potrzebuję tylko długich opcji (tj. krótkie opcje w ogóle nie istnieją), jednak polecenie getopt -l long-key -- "$@" nie „t analizuje poprawnie --long-key opcji. Jak więc mogę użyć polecenia getopt z tylko długimi opcjami? A może to niemożliwe, czy jest to tylko błąd polecenia getopt?

Komentarze

  • Ty tag dla wewnętrznego getopts, ale używasz polecenia /usr/bin/getopt.
  • @Anthon Przepraszam, użyłem niewłaściwy tag, ale nie ' nie mam wystarczającej reputacji, aby dodać kolejny tag, który wymaga 300 reputacji. Jednak właśnie usunąłem niewłaściwy tag.

Odpowiedź

getopt jest w porządku bez krótkich opcji. Ale musisz mu powiedzieć, że nie masz krótkich opcji. Jest to dziwactwo w składni – z instrukcji:

Jeśli nie -o lub --options znajduje się w pierwszej części, pierwszy parametr drugiej części jest używany jako krótki ciąg opcji.

Tak właśnie dzieje się w Twoim teście: getopt -l long-key -- --long-key foo traktuje --long-key jako listę opcji -egklnoy i foo jako jedyny argument. Użyj

getopt -o "" -l long-key -- "$@" 

np.

$ 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" 

Komentarze

  • Czy w OP ' było mieszanie getopts i getopt infekują Twoją odpowiedź? Zacznij od komentowania getopts, a następnie wspomnij tylko o getopt.
  • @Anthon Cała moja odpowiedź dotyczy programu getopt z GNU coreutils, o co chodzi w tym pytaniu. I poprawiliśmy tekst getopts. Dzięki. getopts nie ' nie obsługuje nawet długich opcji, więc nic z tego nie dotyczy getopts .
  • OP pierwotnie miał tag getopts. Nie chciałem zmieniać Twojej odpowiedzi, ponieważ zwykle wiesz dużo lepiej niż ja, o czym piszesz 🙂
  • Straciłem prawie godzinę, próbując to rozgryźć. Właśnie uratowałeś mi kilka próżnych łyków kawy. Dzięki … użyjmy teraz lepiej tej kawy. ☕️

Odpowiedź

Nie mam pojęcia o getopt, ale getopts może być używany do obsługi tylko długich opcji, takich jak ta:

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

Oczywiście, i tak nie jest ” Nie działa, jeśli opcje długie mają mieć argumenty. Można to zrobić, ale, jak się nauczyłem, pracuję nad tym. Chociaż początkowo umieściłem to tutaj, zdałem sobie sprawę, że w przypadku długich opcji nie ma zbyt dużej użyteczności. W tym przypadku było to tylko skrócenie mojego case (match) pola przez pojedynczy, przewidywalny znak. Teraz wiem, że jest doskonały do krótkich opcji – jest najbardziej przydatny, gdy pętla obejmuje ciąg o nieznanej długości i wybiera pojedyncze bajty zgodnie z ciągiem opcji . Ale gdy opcja jest argumentem, „niewiele można zrobić” z kombinacją for var do case $var in, którą mogłaby zrobić. Lepiej jest, Myślę, że dla uproszczenia.

Podejrzewam, że to samo dotyczy getopt, ale nie wiem o tym wystarczająco dużo, aby powiedzieć z całą pewnością. Biorąc pod uwagę następującą tablicę arg, pokażę mój własny mały parser arg – który zależy głównie od relacji ewaluacja / przypisanie, którą doceniłem dla alias i $((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 

To jest argument, z którym będę pracować. Teraz:

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 

To przetwarza tablicę arg na jeden z dwóch różnych sposobów, w zależności od tego, czy podajesz jej jeden, czy dwa zestawy argumentów oddzielone separatorem --. W obu przypadkach ma to zastosowanie do sekwencji przetwarzania w tablicy arg.

Jeśli nazwiesz ją w ten sposób:

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

Pierwsza kolejność biznes będzie zapisywał swoją funkcję acase() w następujący sposób:

acase() case "$a" in (--lopt1) f=lopt1; aset "?$(($f)):";; (--lopt2) f=lopt2; aset "?$(($f)):";; (--*) f=ignored; aset "?$(($f)):";; (*) aset "" "$a";;esac 

A obok shift 3.Podstawienie polecenia w definicji funkcji acase() jest oceniane, gdy wywołująca powłoka tworzy dokumenty wejściowe funkcji, ale acase() jest nigdy nie była wywoływana ani definiowana w wywołującej powłoce. Oczywiście jest ona wywoływana w podpowłoce, więc w ten sposób możesz dynamicznie określać interesujące opcje w linii poleceń.

Jeśli podasz mu tablica bez ograniczeń, po prostu wypełnia acase() dopasowaniami dla wszystkich argumentów zaczynających się od ciągu --.

Funkcja praktycznie całe swoje przetwarzanie wykonuje w podpowłoce – iteracyjnie zapisuje każdą z wartości argumentów do aliasów, którym przypisano nazwy asocjacyjne. Po zakończeniu wypisuje każdą zapisaną wartość za pomocą alias – która jest określona w standardzie POSIX, aby wypisać wszystkie zapisane wartości cytowane w taki sposób, że ich wartości można ponownie wprowadzić do powłoki. Więc kiedy to zrobię …

aopts --lopt1 --lopt2 -- "$@" 

Jego dane wyjściowe wyglądają następująco:

...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" 

Podczas przeglądania listy argumentów sprawdza dopasowanie do bloku wielkości liter. Jeśli znajdzie tam dopasowanie, wyświetla flagę – f=optname. Dopóki ponownie nie znajdzie prawidłowej opcji, będzie dodawać każdy kolejny argument do tablicy, którą buduje na podstawie bieżącej flagi. Jeśli ta sama opcja zostanie podana wiele razy, wyniki są łączone i nie zastępują. Wszystko, co nie jest przypadkowe – lub wszelkie argumenty następujące po zignorowanych opcjach – są przypisywane do tablicy ignorowanej .

Dane wyjściowe są automatycznie zabezpieczane przez powłokę przed wejściem przez powłokę, i tak :

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

… powinno być całkowicie bezpieczne. Jeśli z jakiegoś powodu nie jest bezpieczne, to prawdopodobnie powinieneś zgłosić błąd do opiekuna powłoki.

Przypisuje dwa rodzaje wartości aliasów dla każdego dopasowania. Po pierwsze, ustawia flagę – ma to miejsce niezależnie od tego, czy opcja poprzedza niezgodne argumenty, czy nie. Zatem każde wystąpienie --flag na liście argumentów spowoduje wywołanie flag=1. To się nie komplikuje – --flag --flag --flag po prostu pobiera flag=1. Jednak wartość ta zwiększa – dla wszelkich argumentów, które mogą po niej występować. Może być używany jako klucz indeksu. Po wykonaniu czynności eval powyżej mogę:

printf %s\\n "$lopt1" "$lopt2" 

… aby uzyskać …

8 1 

I tak:

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 

WYJŚCIE

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 

A argumenty, które nie pasują, podstawiłbym ignorowane w powyższym polu for ... in, aby uzyskać:

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 

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *