Det finns ett getopt
-kommando i bash-kommandoraden. getopt
kan användas med korta alternativ (som getopt -o axby "$@"
) och kan användas med både korta och långa alternativ (som getopt -o axby -l long-key -- "$@"
), men nu behöver jag bara långa alternativ (dvs. korta alternativ finns inte alls), men kommandot getopt -l long-key -- "$@"
tolkar inte --long-key
alternativet korrekt. Så hur kan jag använda getopt
-kommandot med endast långa alternativ? Eller är det omöjligt eller är det bara ett fel i kommandot getopt
?
Kommentarer
Svar
getopt
är helt bra med inga korta alternativ. Men du måste säga att du inte har några korta alternativ. Det är en konstig syntax – från manualen:
Om inget
-o
eller--options
alternativet finns i den första delen, den första parametern i den andra delen används som korta alternativsträng.
Det är vad som händer i ditt test: getopt -l long-key -- --long-key foo
behandlar --long-key
som listan över alternativ -egklnoy
och foo
som enda argument. Använd
getopt -o "" -l long-key -- "$@"
t.ex.
$ 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"
Kommentarer
- Gjorde OP ' s blandning av
getopts
ochgetopt
infekterar till ditt svar? Du börjar med att kommenteragetopts
och bara nämnagetopt
. - @Anthon Allt mitt svar handlar om
getopt
-programmet från GNU coreutils vilket är vad frågan handlar om. I har fixat texten som sägergetopts
. Tack.getopts
gör ' inte ens långa alternativ, så inget av detta skulle gälla förgetopts
. - OP hade ursprungligen taggen
getopts
. Jag ville inte ändra ditt svar, för du vet normalt mycket bättre än jag vad du skriver om 🙂 - Jag tappade nästan en timme och försökte räkna ut det här. Du räddade mig bara några få fåfänga kaffe. Tack … låt oss använda det här kaffet bättre nu. ☕️
Svar
Dunno about getopt
men getopts
inbyggt kan användas för att hantera bara långa alternativ som detta:
while getopts :-: o do case "$o$OPTARG" in (-longopt1) process ;; (-longopt2) process ;; esac; done
Naturligtvis, som det är, gör det inte t fungerar om långa alternativ ska ha argument. Det kan dock göras, men eftersom jag har lärt mig att arbeta med detta. Medan jag ursprungligen inkluderade det här insåg jag att det för långa alternativ inte har mycket nytta. I det här fallet förkortade det bara min case
(match)
fält med en enda, förutsägbar karaktär. Nu, vad jag vet, är att det är utmärkt för korta alternativ – det är mest användbart när det går över en sträng av okänd längd och väljer enstaka byte enligt dess alternativsträng Men när alternativet är arg, det gör du inte mycket med en for var do case $var in
kombination som det skulle kunna göra. Det är bättre, Jag tror, för att hålla det enkelt.
Jag misstänker att samma sak gäller för getopt
men jag vet inte tillräckligt om det för att säga med säkerhet. Med tanke på följande arg-array kommer jag att demonstrera min egen lilla arg-parser – som främst beror på utvärderings- / tilldelningsförhållandet jag har uppskattat för alias
och $((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
Det är argsträngen jag kommer att arbeta med. Nu:
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
Det bearbetar arg-arrayen på ett av två olika sätt beroende på om du ger den en eller två uppsättningar av argument åtskilda av --
avgränsare. I båda fallen gäller det sekvenser för bearbetning till arg-arrayen.
Om du kallar det så:
: $((SHIFT$$=3)); aopts --lopt1 --lopt2 -- "$@"
Dess första ordning på verksamheten blir att skriva sin acase()
-funktion så att den ser ut:
acase() case "$a" in (--lopt1) f=lopt1; aset "?$(($f)):";; (--lopt2) f=lopt2; aset "?$(($f)):";; (--*) f=ignored; aset "?$(($f)):";; (*) aset "" "$a";;esac
Och bredvid shift 3
.Kommandosubstitutionen i acase()
funktionsdefinitionen utvärderas när det anropande skalet bygger funktionens inmatning här-dokument, men acase()
är aldrig ringt eller definierat i det anropande skalet. Det kallas dock i subshell, naturligtvis, och så kan du dynamiskt ange alternativen av intresse på kommandoraden.
Om du ger det en oavgränsad matris fyller den helt enkelt acase()
med matchningar för alla argument som börjar med strängen --
.
Funktionen gör praktiskt taget all sin bearbetning i subshell – sparar iterativt vart och ett av arg-värdena i alias tilldelade associerande namn. När det är klart skriver det ut varje värde som sparats med alias
– vilket är POSIX-specificerat för att skriva ut alla sparade värden som citeras på ett sådant sätt att deras värden kan matas in i skalet. Så när jag gör …
aopts --lopt1 --lopt2 -- "$@"
Dess output ser ut så här:
...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"
När den går igenom arg-listan kontrollerar den mot fallblocket för en match. Om den hittar en match där kastar den en flagga – f=optname
. Tills den återigen hittar ett giltigt alternativ kommer den att lägga till varje efterföljande arg till en array som den bygger baserat på den aktuella flaggan. Om samma alternativ anges flera gånger blir resultaten sammansatta och åsidosätter inte. Allt som inte är fallet – eller några argument som följer ignorerade alternativ – tilldelas en ignorerad -matris.
Utgången är skalskyddad för skalinmatning automatiskt av skalet, och så :
eval "$(: $((SHIFT$$=3));aopts --lopt1 --lopt2 -- "$@")"
… borde vara helt säkert. Om det av någon anledning inte är säkert, bör du antagligen skicka en felrapport till din skalhållare.
Den tilldelar två typer av aliasvärden för varje matchning. Först sätter den en flagga – detta inträffar huruvida ett alternativ föregår argument som inte matchar. Så varje förekomst av --flag
i arg-listan kommer att utlösa flag=1
. Detta förenas inte – --flag --flag --flag
blir bara flag=1
. Det här värdet ökar dock emellertid – för alla argument som kan följa det. Den kan användas som en indexnyckel. Efter att ha gjort eval
ovan kan jag göra:
printf %s\\n "$lopt1" "$lopt2"
… för att få …
8 1
Och så:
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
UTGÅNG
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
Och till args som inte matchade skulle jag ersätta ignorerad i ovanstående for ... in
-fält för att få:
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
, men du använder kommandot/usr/bin/getopt
.