Come utilizzare getopt nella riga di comando bash con solo opzioni lunghe?

Cè un comando getopt nella riga di comando di bash. getopt può essere utilizzato con opzioni brevi (come getopt -o axby "$@") e può essere utilizzato sia con opzioni brevi che lunghe (come getopt -o axby -l long-key -- "$@"), ma ora ho bisogno di solo opzioni lunghe (ovvero le opzioni brevi non esistono affatto), tuttavia il comando getopt -l long-key -- "$@" non analizza correttamente lopzione --long-key. Allora come posso utilizzare il comando getopt con solo opzioni lunghe? O è impossibile o è solo un bug del comando getopt?

Commenti

  • Tu tag per il getopts interno, ma stai utilizzando il comando /usr/bin/getopt.
  • @Anthon Spiacenti, ho usato il tag sbagliato, ma non ho ' abbastanza reputazione per aggiungere un altro tag, che richiede 300 reputazioni. Tuttavia, ho appena eliminato il tag sbagliato.

Risposta

getopt va benissimo senza avere opzioni brevi. Ma devi dirgli che non hai opzioni brevi. È “una stranezza nella sintassi – dal manuale:

Se no -o o --options si trova nella prima parte, il primo parametro della seconda parte viene utilizzato come stringa di opzioni breve.

Questo è ciò che sta accadendo nel tuo test: getopt -l long-key -- --long-key foo considera --long-key come lelenco di opzioni -egklnoy e foo come unico argomento. Utilizza

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

ad esempio

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

Commenti

  • LOP ' ha mescolato getopts e getopt infettano la tua risposta? Inizi commentando getopts e poi menziona solo getopt.
  • @Anthon Tutta la mia risposta riguarda il programma getopt di GNU coreutils, che è largomento della domanda. I ho corretto il testo che diceva getopts. Grazie. getopts ' non esegue nemmeno opzioni lunghe, quindi niente di tutto ciò si applicherebbe a getopts .
  • LOP aveva originariamente il tag getopts. Non volevo cambiare la tua risposta, perché normalmente sai molto meglio di me di cosa stai scrivendo 🙂
  • Ho perso quasi unora cercando di capirlo. Mi hai appena risparmiato qualche inutile sorso di caffè. Grazie … ora utilizziamo meglio questo caffè. ☕️

Risposta

Non so di getopt ma il getopts integrato può essere utilizzato per gestire solo opzioni lunghe come questa:

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

Ovviamente, così comè, non funziona ” Non funziona se si suppone che le opzioni lunghe abbiano argomenti. Può essere fatto, però, ma come ho imparato lavorando su questo. Sebbene inizialmente lo includessi qui, mi sono reso conto che per le opzioni lunghe non ha molta utilità. In questo caso si trattava solo di accorciare il mio case (match) campi da un singolo carattere prevedibile. Ora, quello che so, è che è eccellente per le opzioni brevi: è molto utile quando esegue il loop su una stringa di lunghezza sconosciuta e seleziona singoli byte in base alla sua stringa di opzioni . Ma quando lopzione è arg, non stai facendo molto con una combinazione for var do case $var in che potrebbe fare. È meglio, Penso, per mantenerlo semplice.

Sospetto che lo stesso sia vero per getopt ma non ne so abbastanza per poterlo dire con certezza. Dato il seguente array arg, dimostrerò il mio piccolo parser arg, che dipende principalmente dalla relazione di valutazione / assegnazione che “ho imparato ad apprezzare per alias e $((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 

Questa è la stringa arg con cui lavorerò. Ora:

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 

che elabora larray arg in uno dei due modi diversi a seconda che gli venga consegnato uno o due set di argomenti separati dal delimitatore --. In entrambi i casi si applica alle sequenze di elaborazione dellarray arg.

Se lo chiami come:

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

Il suo primo ordine di lazienda scriverà la sua funzione acase() simile a:

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

e accanto a shift 3.La sostituzione del comando nella definizione della funzione acase() viene valutata quando la shell chiamante crea la funzione “s input here-documents, ma acase() è mai chiamato o definito nella shell chiamante. Viene chiamato nella subshell, però, ovviamente, e quindi in questo modo puoi specificare dinamicamente le opzioni di interesse sulla riga di comando.

Se gli dai un array non delimitato, popola semplicemente acase() con corrispondenze per tutti gli argomenti che iniziano con la stringa --.

La funzione esegue praticamente tutta la sua elaborazione nella subshell, salvando iterativamente ciascuno dei valori degli argomenti in alias assegnati con nomi associativi. Quando è finito, stampa ogni valore salvato con alias – che è specificato da POSIX per stampare tutti i valori salvati citati in modo tale che i loro valori possano essere reinseriti nella shell. Quindi quando lo faccio …

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

Il suo output è simile a questo:

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

Mentre scorre la lista degli argomenti, controlla il blocco del caso per una corrispondenza. Se trova una corrispondenza, lancia un flag – f=optname. Finché non trova di nuovo unopzione valida, aggiungerà ogni argomento successivo a un array che costruisce in base al flag corrente. Se la stessa opzione viene specificata più volte, i risultati si sommano e non vengono sovrascritti. Tutto ciò che non è nel caso, o qualsiasi argomento che segue le opzioni ignorate, viene assegnato a un array ignorato .

Loutput è protetto dalla shell per linput della shell automaticamente dalla shell, e così :

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

… dovrebbe essere perfettamente al sicuro. Se per qualsiasi motivo non è sicuro, allora dovresti probabilmente inviare una segnalazione di bug al manutentore della shell.

Assegna due tipi di valori alias per ogni corrispondenza. Innanzitutto, imposta un flag: ciò si verifica se unopzione precede o meno argomenti non corrispondenti. Pertanto, qualsiasi occorrenza di --flag nellelenco degli argomenti attiverà flag=1. Questo non è composto – --flag --flag --flag ottiene solo flag=1. Questo valore non aumenta però, per qualsiasi argomento che potrebbe seguirlo. Può essere utilizzato come chiave di indice. Dopo aver eseguito eval sopra posso fare:

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

… per ottenere …

8 1 

E così:

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 

OUTPUT

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 

E agli argomenti che non corrispondono, sostituirei ignorato nel campo for ... in sopra per ottenere:

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 

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *