Attenzione! Questa è una Pagina di prova. Le informazioni riportate potrebbero essere incomplete, errate e potenzialmente pericolose. Per contribuire alla realizzazione di questa pagina consultare la discussione di riferimento. |
Problemi in questa pagina? Segnalali in questa discussione
Introduzione
Il parsing è la procedura con cui un comando, uno script o più genericamente un programma, analizza le istruzioni che l'utente fornisce quando lo chiama usando il terminale.
Queste istruzioni sono una opportuna combinazione di opzioni e parametri e modificano il comportamento del programma a cui vengono passate.
A esempio, per lanciare il comando ls da terminale una sinossi comune è la seguente:
ls -a -l --color=auto file
Ul generico comando è composto dal nome del comando stesso seguito da una serie di argomenti detti parametri posizionali.
Questi ultimi possono essere suddivisi in alcune categorie logiche:
opzioni corte: consistono in un trattino seguito da una singola lettera (ad esempio -a nel comando precedente). Se vi sono più opzioni corte, le si può raggruppate insieme (ad esempio -al nel comando precedente).
opzioni lunghe: possono essere espresse nello stile Gnu, consistente in due trattini seguiti dal nome completo dell'opzione (as esempio --color nel comando precedente), oppure nello stile XF86, che prevede un singolo trattino seguito dal nome completo dell'opzione.
parametri: specificano le azioni che devono svolgere le opzioni e vengono scritti subito dopo di esse. Generalmente, per le opzioni corte sono scritti direttamente attaccati all'opzione o separatati da uno spazio; per le opzioni lunghe sono separati da uno spazio o dal carattere «=» (ad esempio , =auto nel comando precedente).
Il comando precedente è equivalente al seguente:
ls --color=auto -la file
In questo caso tutte le opzioni corte sono state riunite ed è stato cambiato l'ordine delle opzioni.
Non tutti i comandi accettano un ordine casuale delle opzioni.
La difficoltà del parsing è proprio nel riconoscimento delle varie opzioni e parametri, a prescindere dalla loro posizione nella sintassi della chiamata.
Per agevolare questa procedura, sono disponibili due utili comandi da usare nel terminale:
getopt: ## concisa descrizione del comando
getopts: ## concisa descrizione del comando
getopt
getopt analizza una stringa di parametri e ne restituisce una versione canonica e standardizzata che può essere analizzata più facilmente. Questa analisi successiva viene eseguita in un ciclo while, all'interno del quale le varie scelte sono effettuate in un costrutto case.
Pro
Supporta sia le opzioni corte, sia quelle lunghe. Di quest'ultime, supporta sia lo stile GNU, sia lo stile XF86.
- È in grado di gestire opzioni con parametri che siano obbligatori o facoltativi.
Contro
La versione descritta in questa guida non è un built-in della shell, ma è inclusa nel pacchetto util-linux e precedentemente nella libreria glibc (cioè, la GNU lib c). Questa versione è presente di default in Ubuntu e in tutte le derivate ufficiali. Nelle versioni di Linux in cui non fosse presente, può essere facilmente installata.
Per verificare che la versione nel proprio sistema sia quella descritta in questa guida, si può controllare che l'output del seguente comando sia uguale a 4:getopt --test || echo $?
Sintassi
getopt può essere chiamato con tre diverse sintassi:
- La sintassi minimale è:
getopt optstring parameters
La stringa optstring elenca le opzioni nel formato corto che si possono incontrare durante il parsing. Questa stringa può essere racchiusa tra doppi apici e al suo interno i nomi delle opzioni possono essere elencati senza separatori. Se l'opzione prevede un parametro obbligatorio, il carattere che la indica è seguito dai due punti; se il parametro è opzionale, è seguito da una coppia di doppi punti.
La stringa parameters contiene gli argomenti su cui operare. Generalmente si pone uguale alla stringa dei parametri posizionali passati allo script, cioè "$@". Se si vuole scrivere una stringa alternativa, questa non va racchiusa tra apici, né singoli né doppi. - Un'estensione della precedente sintassi è data da:
getopt [options] [--] optstring parameters
Le parti racchiuse tra parentesi quadre sono opzionali. Se mancano, si riottiene la sintassi precedente.
La stringa options, descritta in dettaglio nella tabella successiva, contiene una serie di opzioni e rispettivi parametri che modificano il funzionamento del comando. - Infine, la sintassi più articolata è data da:
getopt [options] -o|--options optstring [options] [--] parameters
In questo caso la stringa options tra parentesi quadre può ricorrere due volte.
La stringa -o|--options indica che le opzioni a sinistra e destra della barra verticale sono equivalenti.
La stringa optstring ha lo stesso significato visto nelle precedenti sintassi.
Le opzioni e rispettivi parametri che possono essere presenti nella stringa options tra parentesi quadre sono i seguenti:
Opzione |
Descrizione |
-l|--long|--longoptions longopts |
Indica che durante il parsing si potranno incontrare opzioni lunghe, i cui nomi vengono specificati nella stringa longopts. Questa stringa può essere racchiusa tra doppi apici e i nomi al suo interno vanno separati con una virgola. Se il nome che indica l'opzione lunga è seguito dai due punti, questa accetta un parametro obbligatorio; se è seguito da una coppia di doppi punti, accetta un parametro facoltativo. |
-a|--alternative |
Abilita il riconoscimento delle opzioni lunghe nello stile XF86, quindi con un singolo trattino iniziale. |
-n|--name progname |
Imposta il nome dello script al contenuto della variabile progname. |
-q|--quiet |
Disabilita l'output automatico degli errori. |
-Q|--quiet-output |
Disabilita la produzione della stringa normalizzata dei parametri passati allo script, ma gli errori incontrati nel parsing vengono ancora riportati. |
Esempi di chiamate al comando
I seguenti esempi mostrano come utilizzare le tre sintassi viste nel paragrafo precedente:
- Un esempio di invocazione del comando secondo la sintassi minimale è:
getopt "hf:d::" "$@"
Con questa sintassi vengono accettate le seguenti opzioni: h senza parametro, f con un parametro obbligatorio e d con un parametro facoltativo.
- Un esempio di invocazione del comando con la seconda sintassi è:
getopt -l "help,file:,dir::" -- "hf:d::" "$@"
Con questa sintassi vengono accettate anche le opzioni nella versione lunga: help senza parametro, file con un parametro obbligatorio e dir con un parametro facoltativo.
- Un esempio di invocazione del comando seguendo la sintassi più articolata è:
getopt -l "help,file:,dir::" -o "hf:d::" --alternative -- "$@"
Analogo all'esempio precedente, ma con questa sintassi le opzioni possono essere indicate anche nello stile XF86.
È importante sottolineare che, negli argomenti passati allo script, le opzioni e i relativi parametri rispettano le seguenti regole:
Se un'opzione corta accetta un parametro obbligatorio, opzione e parametro possono essere scritti attaccati o separati da uno spazio.
Se il parametro è facoltativo, devono essere scritti attaccati.Se un'opzione lunga accetta un parametro obbligatorio, opzione e parametro devono essere separati da uno spazio o dal carattere «=».
Se il parametro è facoltativo, devono essere separati dal carattere «=».- Le opzioni corte possono essere raggruppate e scritte in qualsiasi ordine, con l'accortezza di lasciare accoppiate ai propri parametri quelle che li prevedono.
getopt riconosce qualsiasi abbreviazione non ambigua delle opzioni nel formato lungo. Facendo riferimento agli esempi precedenti, l'opzione --fi sarà quindi riconosciuta come --file poiché non c'è rischio di ambiguità con altre opzioni.
Esempio d'uso
Lo script che segue accetta l'opzione -a senza parametro, l'opzione -b con parametro obbligatorio e l'opzione -c con parametro facoltativo. Sono accettate anche le rispettive varianti lunghe nello stile Gnu (ovvero precedute da due trattini) --a-lunga, --b-lunga e --c-lunga.
#! /bin/bash getopt --test > /dev/null if [[ $? -ne 4 ]]; then echo "La versione di getopt non è quella del pacchetto utils-linux" exit 1 fi OPZIONI_CORTE=ab:c:: OPZIONI_LUNGHE=a-lunga,b-lunga:,c-lunga:: TEMP=$(getopt --options="$OPZIONI_CORTE" --longoptions="$OPZIONI_LUNGHE" --name "prova_getopt.sh" -- "$@") if [[ $? -ne 0 ]]; then echo "getopt ha fallito il parsing" exit 2 fi echo "Versione canonica dei parametri prodotta da getopt: "$TEMP"" eval set -- "$TEMP" while true do case "$1" in -a|--a-lunga) echo "Opzione '$1', senza argomento." shift ;; -b|--b-lunga) echo "Opzione '$1', con argomento '$2'." shift 2 ;; -c|--c-lunga) case "$2" in "") echo "Opzione '$1', senza argomento." shift 2 ;; *) echo "Option c, con argomento '$2'." shift 2 ;; esac ;; --) shift break ;; *) echo "Errore interno!" exit ;; esac done if [[ $# != 0 ]] then echo "Argomenti rimanenti:" for i in "$@" do echo "--> '$i'" done fi
Supponendo di aver salvato lo script con il nome prova_getopt.sh, di seguito vengono mostrati due differenti modi di chiamarlo e i rispettivi output prodotti:
Opzione -a più due argomenti:
$ ./prova_getopt.sh foo -a bar Versione canonica dei parametri prodotta da getopt: -a -- 'foo' 'bar' Opzione '-a', senza argomento. Argomenti rimanenti: --> 'foo' --> 'bar'
Il parametro bar non viene associato all'opzione -a, in quanto questa opzione non accetta parametri. Viene invece interpretato come argomento a sé stante ed elencato inseme a foo tra gli argomenti rimanenti dopo il parsing.
Opzione -b con parametro, più due argomenti::
$ ./prova_getopt.sh -b 5 bar foo Versione canonica dei parametri prodotta da getopt: -b '5' -- 'bar' 'foo' Opzione '-b', con argomento '5'. Argomenti rimanenti: --> 'bar' --> 'foo'
Per foo e bar si ha lo stesso comportamento del comando precedente. L'argomento 5 invece viene interpretato come parametro dell'opzione -b. Visto che per questa opzione il parametro è obbligatorio, si può usare la sintassi equivalente -b5.
Considerazioni sull'esempio d'uso
I due modi di chiamare lo script d'esempio illustrati nel paragrafo precedente possono essere accorpati come segue:
$ ./prova_getopt.sh -ab5 foo bar Versione canonica dei parametri prodotta da getopt: -a -b '5' -- 'foo' 'bar' Opzione '-a', senza argomento. Opzione '-b', con argomento '5'. Argomenti rimanenti: --> 'foo' --> 'bar'
La posizione dei parametri passati allo script può variare secondo tutte le combinazioni consistenti con la sintassi descritta precedentemente, ad esempio:
$ ./prova_getopt.sh foo -b5 bar -a Versione canonica dei parametri prodotta da getopt: -b '5' -a -- 'foo' 'bar' Opzione '-b', con argomento '5'. Opzione '-a', senza argomento. Argomenti rimanenti: --> 'foo' --> 'bar'
L'opzione con parametro obbligatorio -b ha due sintassi equivalenti in versione lunga:
$ ./prova_getopt.sh --b-lunga 5
$ ./prova_getopt.sh --b-lunga=5
L'unica sintassi valida per l'opzione corta -c, che accetta un parametro opzionale, è quella in cui opzione e parametro sono scritti attaccati:
$ ./prova_getopt.sh -c6
La sintassi equivalente dell'opzione -c in versione lunga richiede che opzione e parametro siano separati dal carattere «=»:
$ ./prova_getopt.sh --c-lunga=6
- Se si vogliono passare parametri posizionali interpretabili come opzioni, questi vanno scritti dopo i doppi trattini:
$ ./prova_getopt.sh foo -ab5 -- -car -bar -6 Versione canonica dei parametri prodotta da getopt: -a -b '5' -- 'foo' '-car' '-bar' '-6' Opzione '-a', senza argomento. Opzione '-b', con argomento '5'. Argomenti rimanenti: --> 'foo' --> '-car' --> '-bar' --> '-6'
Senza il doppio trattino, infatti, -6 verrebbe interpretata come opzione non permessa, mentre -car e -bar rispettivamente come opzioni -c e -b, entrambe con ar come parametro.
La porzione di codice:
getopt --test > /dev/null if [[ $? -ne 4 ]]; then echo "La versione di getopt non è quella del pacchetto utils-linux" exit 1 fi
serve per verificare che la versione di getopt sia quella descritta in questa guida. È superflua per Ubuntu e le sue derivate, ma permette di verificare che lo script sia correttamente utilizzabile anche in altre generiche distribuzioni di Linux.
La riga seguente visualizza la riorganizzazione dei parametri nella versione canonica prodotta da getopt:
echo "Versione canonica dei parametri prodotta da getopt: "$TEMP" "
Ai fini del funzionamento dello script è superflua.L'istruzione seguente sostituisce i parametri posizionali passati allo script con la stringa TEMP, che è la loro versione canonica:
eval set -- "$TEMP"`
In alcuni script è possibile che manchi l'istruzione eval. In questo caso, sono script non portabili pensati per BSD. La versione in cui è presente anche eval risulta portabile e deve essere preferita. Infine, le doppie virgolette che racchiudono $TEMP sono fondamentali.
Il comando shift viene utilizzato all'interno del costrutto case per avanzare nella lettura dei parametri posizionali passati allo script. Questo comando ha l'effetto di far slittare verso sinistra i parametri posizionali, eliminandoli:
- Nel caso di un'opzione senza parametro, si slitta di una posizione.
Nel caso dei due trattini -- si slitta di una posizione e si interrompe il parsing.
- Nel caso di una opzione con parametro facoltativo, si slitta di due posizioni.
Il costrutto case nidificato è un esempio di come si gestisce un'opzione che ha un parametro facoltativo.
getopts
Pro
- È un built-in della shell, quindi:
- è definito nello standard POSIX e non c'è bisogno di fare attenzione alle sue diverse implementazioni.
- Non ha bisogno di programmi esterni per accedere ai parametri posizionali.
- Può avere accesso diretto alle variabili della shell, che può usare per fare il parsing.
Contro
Può fare il parsing solo delle opzioni corte. Se lo script prevede l'uso di opzioni lunghe, sia nello stile GNU sia nello stile XF86, getopts non è la scelta opportuna.
- Non supporta le opzioni con parametri facoltativi.
Funzionamento
La sintassi del comando è:
getopts optstring varname [args]
in cui
Parametro |
Descrizione |
optstring |
È una stringa che istruisce il comando relativamente a quali opzioni aspettarsi e quando siano previsti i relativi parametri. |
varname |
È una stringa che rappresenta il nome della variabile della shell in cui il comando getopts inserisce il valore delle opzioni lette. |
args |
È una stringa facoltativa. Di default, il comando fa il parsing dei parametri posizionali, cioè della stringa $@; se è presente una stringa args, farà il parsing dei parametri contenuti in quest'ultima. |
getopts viene quindi istruito sulle opzioni e gli eventuali parametri che si può aspettare tramite optstring.
La sintassi di questa stringa è molto semplice.
- Ogni opzione che si vuole aggiungere, viene indicata con la relativa lettera. Non sono consentiti i caratteri ':' e '?'.
Se l'opzione prevede un parametro associato, il carattere che la indica è seguito dai due punti. Quindi, se si vuole permettere l'opzione 'f' con un parametro associato, nella stringa optstring si aggiungerà la sequenza 'f:'
Il comando getopts può essere impostato nella modalità silenziosa, usando i due punti ':' come prefisso di optstring. Se questo carattere è assente, getopts è in modalità verbosa. La differenza è che nel seconda modalità produce messaggi di errori automatici durante il parsing. Generalmente, si opta per la modalità silenziosa e la gestione autonoma dei messaggi d'errore. Inoltre, tramite la variabile OPTERR (vedi sotto) si può comunque silenziare la stampa dei messaggi di errore nella modalità verbosa.
Quindi, se desideriamo che il comando si aspetti di incontrare nel parsing le opzione 'v' ed 'h' senza parametri associati e l'opzione 'f' con un parametro associato, optstring sarebbe uguale a "vhf:" per la modalità verbosa e uguale a ":vhf:" per quella silenziosa.
Per il suo funzionamento, getopts usa le seguenti variabili:
Variabile |
Descrizione |
OPTIND |
È una variabile della shell, che contiene l'indice del parametro posizionale del prossimo argomento da processare. Viene modificata da getopts, che la usa per tenere traccia del proprio stato. Torna utile anche per poter fare uno shift dei parametri posizionali, dopo averli analizzati con getopts. |
OPTARG |
Se è previsto un parametro, viene impostata al valore si quest'ultimo. Se l'opzione non prevede un parametro, è lasciato vuoto. Se l'opzione passata è sconosciuta, viene impostato al valore dell'opzione. |
OPTERR |
È una variabile di bash, che può assumere il valore 1 o 0, a seconda che si desideri o meno che la shell mostri il messaggio d'errore generato automaticamente da getopts. Di default è impostata ad 1 e ha senso modificarla solo nella modalità verbosa. Non è supportata in shell quali ksh93, mksh, zsh, or dash. |
La gestione del parametro varname e della variabile OPTARG cambia a seconda della modalità in cui si usa il comando ed è riassunta nella seguente tabella:
Modalità verbosa |
||
opzione |
varname è posto uguale al carattere '?' |
|
parametro obbligatorio |
varname è posto uguale al carattere '?' |
Modalità silenziosa |
||
opzione |
varname è posto uguale al carattere '?' |
|
parametro obbligatorio |
varname è posto uguale al carattere ':' |
Esempi d'uso
Il comando viene generalmente usato all'interno di un ciclo while: a ogni iterazione, legge dalla stringa dei parametri posizionali (o da args se fornito) un'opzione e il suo eventuale parametro; se tutto è corretto, aggiorna le sue variabili interne, secondo quanto visto al punto precedente, e fornisce questi dati all'utente per la loro analisi, che viene svolta all'interno di un costrutto case. Il comando restituisce un exit status uguale a 1, interrompendo il ciclo while, quando incontra il primo parametro opzionale che non corrisponde a una opzione prevista.
Modalità verbosa
Lo script che segue è un esempio di utilizzo del comando nella modalità verbosa. Lo script accetta le opzioni 'a' e 'c' senza parametro e le opzioni 'b' e 'd' con un parametro. Le opzioni 'b' e 'd' non sono implementate, per simulare una situazione frequente nelle fasi iniziali di sviluppo di uno script.
#!/bin/bash while getopts "ab:cd:" option do case $option in a) echo "Opzione -$option, senza argomento" echo "OPTIND è $OPTIND" echo "*** *** ***" ;; b) echo "Opzione -$option, con argomento '"$OPTARG"'" echo "OPTIND è $OPTIND" echo "*** *** ***" ;; \?) echo "OPTIND è $OPTIND" >&2 echo "Termino" >&2 exit 1 ;; *) echo "Opzione non ancora implementata: -$option" echo "OPTIND è $OPTIND" echo "*** *** ***" ;; esac done shift $((OPTIND - 1)) if [[ $# -ne 0 ]] then echo "Rimangono da analizzare i parametri:" for i in "$@" do echo "--> '$i'" done fi
Supponendo di aver salvato lo script con il nome prova_getopts_verboso.sh, alcuni esempi dell'output generato da questo script sono:
$ ./prova_getopts_verboso.sh -a Opzione -a, senza argomento *** *** *** Il valore di OPTIND è 2
in cui allo script è fornita solo l'opzione 'a'.$ ./prova_getopts_verboso.sh -ab 10 Opzione -a, senza argomento OPTIND è 1 *** *** *** Opzione -b, con argomento '10' OPTIND è 3 *** *** ***
in cui è fornita anche l'opzione 'b' con il parametro '10'.- L'ordine delle opzioni può essere cambiato in modo consistente con la sintassi, quindi il comando precedente equivale a:
$ ./prova_getopts_verboso.sh -b 10 -a Opzione -b, con argomento '10' OPTIND è 3 *** *** *** Opzione -a, senza argomento OPTIND è 4 *** *** ***
$ ./prova_getopts_verboso.sh -d 10 -a Opzione non ancora implementata: -d OPTIND è 3 *** *** *** Opzione -a, senza argomento OPTIND è 4 *** *** ***
in cui viene passata l'opzione non implementata '-d' con il parametro '10' - Infine, se si passa un'opzione non valida, getopts produce automaticamente un messaggio d'errore:
$ ./prova_getopts_verboso.sh -f ./prova_getopts_verboso.sh: opzione illecita -- f OPTIND è 2 Termino
Modalità sileziosa
Lo script seguente è l'analogo del precedente, ma in modalità silenziosa:
#!/usr/bin/env bash while getopts ":ab:cd:" option; do case $option in a) echo "Opzione -$option, senza argomento" echo "OPTIND è $OPTIND" echo "*** *** ***" ;; b) echo "Opzione -$option, con argomento '"$OPTARG"'" echo "OPTIND è $OPTIND" echo "*** *** ***" ;; \?) echo "Opzione non valida: -$OPTARG" >&2 echo "OPTIND è $OPTIND" >&2 echo "Termino" >&2 exit 1 ;; : ) echo "-$OPTARG richede un parametro, che non è stato passato" >&2 echo "OPTIND è $OPTIND" >&2 echo "Termino" >&2 exit 1 ;; *) echo "Opzione non ancora implementata: -$option" echo "OPTIND è $OPTIND" echo "*** *** ***" esac done shift $((OPTIND -1)) if [[ $# -ne 0 ]] then echo "Rimangono da analizzare i parametri:" for i in "$@" do echo "--> '$i'" done fi
Differenze dallo script precedente ci sono solo per opzioni non valide e parametri obbligatori non passati. Se lo script è stato salvato col nome prova_getopts_silenzioso.sh, si ha rispettivamente:
$ ./prova_getopts_silenzioso.sh -f Opzione non valida: -f OPTIND è 2 Termino
$ ./prova_getopts_silenzioso.sh -b -b richede un parametro, che non è stato passato OPTIND è 2 Termino
Considerazioni sugli esempi d'uso
getopts restituisce un exit status pari a 1 se incontra un parametro che non corrisponde a una opzione prevista. Per gli script in questi esempi, è quindi necessario scrivere per ultimi i parametri che si vogliono analizzare a valle del ciclo while. Se questi sono interpretabili come opzioni, vanno separati da un doppio trattino. In realtà, è possibile una gestione più articolata di questa tipologia di parametri, che però esula dalle finalità di questa guida introduttiva e per la quale si rimanda alla sezione programmazione del forum.
Nella versione verbosa, il caso \?) gestisce sia un'opzione non valida sia l'assenza di un parametro obbligatorio. La stampa dell'errore è gestita direttamente dal comando getopts, che continua il parsing dopo aver comunicato l'errore. Se si desidera che il programma termini la sua esecuzione in questi due casi, deve essere richiesto esplicitamente, ad esempio con il comando exit.
- Nella versione silenziosa, il caso \?) gestisce un'opzione non valida e il caso : ) l'assenza di un parametro obbligatorio.
- Il caso *) permette di gestire eventuali opzioni non ancora implementate all'interno dello script. Nei due esempi, le opzioni non ancora implementate sono 'b' e 'd'. Questa modalità risulta molto comoda nella fase iniziale di implementazione di uno script.
L'istruzione shift successiva al ciclo while ha lo scopo di eliminare dalla stringa dei parametri posizionali quelli che sono già stati analizzati. Il parametro OPTIND assicura infatti di ottenere lo slittamento verso sinistra di tutti i parametri già letti. In questo modo, lo script è in grado di gestire successivamente anche eventuali ulteriori parametri posizionali.
È utile ricordare che OPTIND contiene l'indice del parametro posizionale del prossimo argomento da processare e quindi non viene incrementato fino ad aver esaurito l'analisi di tutte le opzioni scritte attaccate.