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
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
igetopt
infekują Twoją odpowiedź? Zacznij od komentowaniagetopts
, a następnie wspomnij tylko ogetopt
. - @Anthon Cała moja odpowiedź dotyczy programu
getopt
z GNU coreutils, o co chodzi w tym pytaniu. I poprawiliśmy tekstgetopts
. Dzięki.getopts
nie ' nie obsługuje nawet długich opcji, więc nic z tego nie dotyczygetopts
. - 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
getopts
, ale używasz polecenia/usr/bin/getopt
.