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


13

C'è un getoptcomando nella riga di comando bash. getoptpuò essere utilizzato con opzioni brevi (come getopt -o axby "$@") e può essere utilizzato con opzioni brevi e lunghe (come getopt -o axby -l long-key -- "$@"), ma ora ho bisogno solo di opzioni lunghe (ovvero le opzioni brevi non esistono affatto), tuttavia il comando getopt -l long-key -- "$@"non --long-keyopzione di analisi corretta. Quindi, come posso usare il getoptcomando con solo opzioni lunghe? O è impossibile o è solo un bug del getoptcomando?


Si tag per l'interno getopts, ma si sta utilizzando il /usr/bin/getoptcomando.
Anthon,

@Anthon Siamo spiacenti, ho usato il tag sbagliato, ma non ho abbastanza reputazioni per aggiungere un altro tag, che richiede 300 reputazioni. Tuttavia, ho eliminato il tag sbagliato proprio ora.
Victor,

Risposte:


15

getoptva benissimo senza avere opzioni brevi. Ma devi dirlo che non hai opzioni brevi. È una stranezza nella sintassi - dal manuale:

Se non viene trovata alcuna opzione -oo --optionsnella prima parte, il primo parametro della seconda parte viene utilizzato come stringa di opzioni brevi.

Questo è ciò che sta accadendo nel tuo test: getopt -l long-key -- --long-key footratta --long-keycome l'elenco di opzioni -egklnoye foocome unico argomento. Uso

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

per 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'

Il PO ha mescolato getoptse getoptinfettato la tua risposta? Inizi con i commenti, getoptspoi fai solo menzione getopt.
Anthon,

@Anthon Tutta la mia risposta riguarda il getoptprogramma di GNU coreutils, che è la domanda. Ho corretto il testo che diceva getopts. Grazie. getoptsnon fa nemmeno lunghe opzioni, quindi nulla di tutto ciò si applicherebbe a getopts.
Gilles 'SO- smetti di essere malvagio' il

Inizialmente l'OP aveva il getoptstag. Non volevo cambiare la tua risposta, perché normalmente sai molto meglio di me di cosa stai scrivendo :-)
Anthon,

Ho perso quasi un'ora cercando di capirlo. Mi hai appena salvato qualche sorso di caffè invano. Grazie ... permettiamo di sfruttare meglio questo caffè. ☕️
dmmd

1

Non so getoptma l' getoptsintegrato può essere usato per gestire solo opzioni lunghe come questa:

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

Naturalmente, così com'è, ciò non funziona se si suppone che le opzioni lunghe abbiano argomenti. Si può fare, però, ma, come ho imparato lavorando su questo. Mentre inizialmente l'ho incluso qui mi sono reso conto che per le opzioni lunghe non ha molta utilità. In questo caso stava solo accorciando i miei case (match)campi di un singolo carattere prevedibile. Ora, quello che so, è che è eccellente per le opzioni brevi - è molto utile quando si esegue il ciclo su una stringa di lunghezza sconosciuta e selezionando singoli byte in base alla sua stringa di opzioni. Ma quando l'opzione è l'arg, non c'è molto che stai facendo con una for var do case $var incombinazione che potrebbe fare. È meglio, penso, per mantenerlo semplice.

Sospetto che lo stesso sia vero, getoptma non ne so abbastanza per dirlo con certezza. Dato il seguente array arg, mostrerò il mio piccolo parser arg - che dipende principalmente dalla relazione di valutazione / assegnazione che ho imparato ad apprezzare per aliase $((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ò. Adesso:

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

Ciò elabora l'array arg in uno di due modi diversi a seconda che tu lo consegni uno o due insiemi di argomenti separati dal --delimitatore. In entrambi i casi si applica alle sequenze di elaborazione nell'array arg.

Se lo chiami come:

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

Il suo primo ordine sarà quello di scrivere la sua acase()funzione per assomigliare 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 dei comandi nella acase()definizione della funzione viene valutata quando la shell chiamante crea qui i documenti di input della funzione, ma acase()non viene mai chiamata o definita nella shell chiamante. Si chiama nella subshell, ovviamente, e quindi in questo modo è possibile specificare dinamicamente le opzioni di interesse sulla riga di comando.

Se lo si passa a un array non delimitato, si popola semplicemente acase()con le 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 di arg in alias assegnati con nomi associativi. Quando è finito, stampa tutti i valori con aliascui è stato salvato , che è specificato da POSIX per stampare tutti i valori salvati citati in modo tale che i loro valori possano essere reintegrati nella shell. Quindi quando lo faccio ...

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

Il suo output è simile al seguente:

...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 l'elenco arg, verifica la corrispondenza con il case case. Se trova una corrispondenza lì lancia una bandiera - f=optname. Fino a quando non troverà di nuovo un'opzione 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 combinano e non sovrascrivono. Tutto ciò che non è nel caso - o qualsiasi argomento che segue le opzioni ignorate - viene assegnato a un array ignorato .

L'output è protetto dalla shell per l'input della shell automaticamente dalla shell, e quindi:

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

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

Assegna due tipi di valori alias per ogni corrispondenza. Innanzitutto, imposta un flag: ciò si verifica indipendentemente dal fatto che un'opzione preceda o meno argomenti non corrispondenti. Quindi si verificherà qualsiasi occorrenza di --flagnell'elenco arg flag=1. Questo non è composto - --flag --flag --flagsolo ottiene flag=1. Questo valore fa però incremento - per qualsiasi argomento che potrebbe seguirlo. Può essere usato come chiave di indice. Dopo aver fatto quanto evalsopra posso fare:

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

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

PRODUZIONE

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 corrispondevano, sostituirei ignorati nel for ... incampo 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.