Hvordan bruke getopt i bash kommandolinje med bare lange alternativer?

Det er en getopt -kommando i bash-kommandolinjen. getopt kan brukes med korte alternativer (for eksempel getopt -o axby "$@"), og kan brukes med både korte og lange alternativer (for eksempel getopt -o axby -l long-key -- "$@"), men nå trenger jeg bare lange alternativer (dvs. korte valg eksisterer ikke i det hele tatt), men kommandoen getopt -l long-key -- "$@" tolker ikke --long-key alternativet riktig. Så hvordan kan jeg bruke getopt kommando med bare lange alternativer? Eller er det umulig, eller er det bare en feil fra getopt -kommandoen?

Kommentarer

  • Du tag for den interne getopts, men du bruker kommandoen /usr/bin/getopt.
  • @Anthon Beklager, jeg har brukt feil tag, men jeg har ikke ' Jeg har ikke nok omdømme til å legge til en ny tag, som trenger 300 omdømme. Jeg har imidlertid slettet feil tag akkurat nå.

Svar

getopt er helt greit uten å ha korte muligheter. Men du må fortelle det at du ikke har noen korte muligheter. Det er en finurlig syntaks – fra manualen:

Hvis ingen -o eller --options alternativet finnes i den første delen, den første parameteren til den andre delen brukes som korte alternativstrenger.

Det som skjer i testen din: getopt -l long-key -- --long-key foo behandler --long-key som listen over alternativer -egklnoy og foo som eneste argument. Bruk

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

f.eks.

$ 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 blanding av getopts og getopt infiserer til svaret ditt? Du begynner med å kommentere getopts og bare nevner getopt.
  • @Anthon Alt svaret mitt handler om getopt -programmet fra GNU coreutils, det er det spørsmålet handler om. Jeg har fikset teksten som sa getopts. Takk. getopts gjør ikke ' ikke engang lange valg, så ingenting av dette vil gjelde for getopts .
  • OPP hadde opprinnelig getopts -koden. Jeg ønsket ikke å endre svaret ditt, for du vet normalt mye bedre enn jeg hva du skriver om 🙂
  • Jeg mistet nesten en time på å prøve å finne ut av dette. Du sparte meg bare noen få forfengelige kaffeslurker. Takk … la oss bruke denne kaffen bedre nå. ☕️

Svar

Dunno about getopt men getopts innebygd kan brukes til å håndtere bare lange alternativer som dette:

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

Selvfølgelig, som det er, gjør det ikke » t fungerer hvis de lange alternativene skal ha argumenter. Det kan imidlertid gjøres, men som jeg har lært å jobbe med dette. Mens jeg først inkluderte det her, innså jeg at det for lange alternativer ikke har mye nytte. I dette tilfellet forkortet det bare case (match) felt med ett enkelt, forutsigbart tegn. Nå, det jeg vet, er at det er utmerket for korte alternativer – det er mest nyttig når det løper over en streng med ukjent lengde og velger enkeltbyte i henhold til alternativstrengen . Men når alternativet er arg, gjør det ikke mye du gjør med en for var do case $var in kombinasjon som det kan gjøre. Det er bedre, Jeg tror, for å holde det enkelt.

Jeg mistenker at det samme gjelder getopt men jeg vet ikke nok om det til å si med sikkerhet. Gitt følgende arg-array, vil jeg demonstrere min egen lille arg-parser – som først og fremst avhenger av evaluerings- / tildelingsforholdet jeg har sett pris på for alias og $((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 er arg-strengen jeg vil jobbe med. Nå:

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 behandler arg-arrayet på en av to forskjellige måter, avhengig av om du gir det ett eller to sett med argumenter atskilt med -- skilletegn. I begge tilfeller gjelder det sekvenser av prosessering til arg-matrisen.

Hvis du kaller det slik:

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

Dens første rekkefølge på virksomheten vil være å skrive acase() -funksjonen slik at den ser ut:

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

Og ved siden av shift 3.Kommandosubstitusjonen i acase() funksjonsdefinisjonen evalueres når det anropende skallet bygger funksjonens inngang her-dokumenter, men acase() er aldri kalt eller definert i det anropende skallet. Det kalles selvfølgelig, og selvfølgelig, og slik kan du dynamisk spesifisere alternativene som er interessante på kommandolinjen.

Hvis du gir det en ikke-avgrenset matrise fyller den bare ut acase() med treff for alle argumenter som begynner med strengen --.

Funksjonen gjør praktisk talt all prosessering i subshell – lagrer iterativt hver av arg-verdiene til aliaser tilordnet assosiative navn. Når det er gjennom, skriver den ut hver verdi den lagret med alias – som er POSIX-spesifisert for å skrive ut alle lagrede verdier som er sitert på en slik måte at deres verdier kan overføres til skallet. Så når jeg gjør …

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

Dets utgang ser slik ut:

...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 gjennom arg-listen, sjekker den mot saksblokken for en kamp. Hvis den finner en kamp der, kaster den et flagg – f=optname. Inntil den igjen finner et gyldig alternativ, vil den legge til hver påfølgende arg i en matrise den bygger basert på gjeldende flagg. Hvis det samme alternativet er angitt flere ganger, blir resultatene sammensatte og overstyrer ikke. Alt som ikke er tilfelle – eller noen argumenter som følger ignorerte alternativer – tilordnes en ignorert matrise.

Utgangen er skallbeskyttet for skallinput automatisk av skallet, og så :

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

… skal være helt trygt. Hvis det av en eller annen grunn ikke er trygt, bør du sannsynligvis sende en feilrapport til skallholderen.

Den tildeler to typer aliasverdier for hver kamp. For det første setter det et flagg – dette skjer om et alternativ går foran argumenter som ikke samsvarer. Så enhver forekomst av --flag i arg-listen vil utløse flag=1. Dette blir ikke sammensatt – --flag --flag --flag får bare flag=1. Denne verdien øker skjønt – for eventuelle argumenter som kan følge den. Den kan brukes som en indeksnøkkel. Etter å ha gjort eval ovenfor kan jeg gjøre:

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

… for å få …

8 1 

Og 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 

UTGANG

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 

Og til argumenter som ikke samsvarte, ville jeg erstattet ignorert i for ... in -feltet ovenfor for å 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 

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *