Problemi in questa pagina? Segnalali in questa discussione
Introduzione
Prima di vedere nel dettaglio gli strumenti che le varie shell disponibili su Ubuntu forniscono per fare il parsing della riga di comando, è utile fare una breve introduzione per capire alcuni concetti fondamentali.
Facendo riferimento al comando tar, un modo generico per lanciare questo comando nel terminale è dato da:
tar -cv -f mioArchivio.tar directoryDaArchiviare
Il comando è composto da una serie di stringhe dette argomenti. Il primo argomento è il nome stesso del comando, mentre i successivi sono indicati con il nome di parametri posizionali.
Questi ultimi possono essere suddivisi in alcune categorie logiche:
una opzione è un argomento, generalmente incluso nella documentazione del comando, la cui presenza modifica il comportamento del comando stesso.
Una opzione può esistere nella versione corta, che consiste in un trattino seguito da una singola lettera (ad esempio, '-f' nel comando precedente), o nella versione lunga. Di quest'ultima, si hanno due formati: nello stile GNU si ha un doppio trattino seguito da una serie di due o più lettere (ad esempio '--help'); lo stile XF86 ha un singolo trattino come prefisso (ad esempio '-verbose').
Più opzioni corte possono essere raggruppate in un singolo argomento (ad esempio, '-cv' nel comando precedente).un parametro è un argomento che può fornire informazioni aggiuntive al comando o a un'opzione a cui si riferisce. Nel comando precedente, 'mioArchivio.tar' è un parametro dell'opzione '-f', mentre 'directoryDaArchiviare' è un parametro per il comando 'tar'. Qualora si volesse aggiungere al comando un parametro che ha l'aspetto di una opzione, ma non la sua funzione, è possibile separarlo dal resto del comando scrivendolo dopo un doppio trattino '--'
Fatta questa premessa, è utile notare che il comando precedente è equivalente al comando:
tar -vcf mioArchivio.tar directoryDaArchiviare
In questo comando, tutte le opzioni corte sono state riunite e ne è stato cambiato l'ordine. Analogamente, l'ordine con cui vengono scritte le opzioni lunghe, insieme ai loro eventuali parametri, non ne altera il significato.
Il parsing della riga di comando è quindi la procedura con cui un comando, o uno script, interpretano le istruzioni fornite dall'utente tramite una opportuna combinazione di opzioni e parametri. Nel caso di uno script, per poter effettuare facilmente questo parsing, si possono usare due strumenti molto versatili: getopt e getopts (fare attenzione alla 's' finale nel secondo comando).
getopt
Il comando getopt analizza una stringa di parametri e ne restituisce una versione canonica, ossia una versione standardizzata che potrà poi essere analizzata più facilmente. Generalmente, questa analisi successiva viene eseguita in un ciclo while, all'interno del quale si esegue un costrutto case.
Pro
getopt supporta sia le opzioni corte, sia quelle lunghe. Di quest'ultime, supporta sia lo stile GNU, sia lo stile XF86.
Contro
Non è un built-in della shell, ma è attualmente incluso nel pacchetto util-linux e precedentemente nella glibc (cioè, la GNU lib c). Ciononostante, può essere facilmente installato nelle distribuzioni in cui non sia presente di default. Inoltre, il comando
getopt --test
restituisce un exit code uguale a 4, se la versione installata è quella descritta in questa guida, e può quindi essere usato per un test preliminare all'interno dello script.
Funzionamento
Il comando getopt può essere chiamato con tre diverse sintassi. La più semplice è:
getopt optstring parameters
La stringa optsting elenca le opzioni nel formato corto che il comando può incontrare durante il parsing. Questa stringa può essere racchiusa tra doppi apici. Al suo interno i nomi delle opzioni possono essere elencati senza separatori. Se il carattere che indica l'opzione è seguito dai due punti, l'opzione prevede un parametro obbligatorio; se è seguito da una coppia di doppi punti, il parametro è opzionale. parameters è la stringa degli argomenti su cui operare. Generalmente si indica come parameters la stringa dei parametri posizionali passati allo script, cioè "$@"; se si vuole passare una stringa alternativa, questa non va racchiusa tra apici, né singoli né doppi.
Le altre due sintassi hanno rispettivamente la forma
getopt [options] [--] optstring parameters
e
getopt [options] -o|--options optstring [options] [--] parameters
optstring e parameters sono gli stessi visti nella prima sintassi, mentre le parti racchiuse tra le parentesi quadre sono opzionali e la barra verticale indica che l'opzione alla sua sinistra e quella alla sua destra sono equivalenti.
La stringa options, che nella seconda sintassi può essere presente in due punti, contiene una serie di opzioni e rispettivi parametri che modificano il funzionamento del comando. Tra queste, le più importanti sono:
-l|--long|--longoptions longopts
L'opzione '-l', o le equivalenti '--long' e '--longoptions', indicano 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. Inoltre, 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
Vengono riconosciute le 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
L'output degli errori da parte di getopt è disabilitato.
-Q|--quiet-output
Non viene prodotto l'output di getopt, ovvero la stringa normalizzata dei parametri passati allo script, ma gli errori incontrati nel parsing vengono ancora riportati.
Esempi di chiamate al comando
I seguenti esempi d'uso mostrano come chiamare getopt facendo riferimento, rispettivamente, alle tre sintassi viste:
getopt "hf:d::" "$@"
Con questa chiamata, getopt potrà accettare l'opzione 'h' senza parametro, l'opzione 'f' con un parametro obbligatorio e l'opzione 'd' con un parametro facoltativo.
getopt -a -l "help,file:,dir::" -- "hf:d::" "$@"
Ora, getopt accetta anche le opzioni nella versione lunga 'file', con un parametro obbligatorio, e 'dir' con un parametro facoltativo.
getopt getopt -l "help,file:dir::" -o "hf:d::" -a -- "$@"
getopt accetta le stesse opzioni del caso precedente, ma queste possono esse indicate anche nello stile XF86.
È importante sottolineare che, negli argomenti passati allo script, opzioni e relativi parametri rispettano le seguenti regole:
- Se un'opzione corta accetta un parametro facoltativo, opzione e parametro possono essere scritti attaccati o separati da uno spazio. Se il parametro è obbligatorio, devono essere scritti attaccati.
- Se un'opzione lunga accetta un parametro facoltativo, opzione e parametro possono solo essere separati da uno spazio o dal carattere '='. Se il parametro è obbligatorio, 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: '--fi' sarà quindi riconosciuta come --file nel caso non ci sia rischio di ambiguità con altre opzioni.
Esempio d'uso
Lo script che segue accetta l'opzione senza parametro e le opzioni e con un parametro obbligatorio. Sono accettate anche le rispettive varianti lunghe , , .
#!/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=dfo:v OPZIONI_LUNGHE=debug,force,output:,verbose PARSED=$(getopt --options=$OPZIONI_CORTE --longoptions=$OPZIONI_LUNGHE --name "$0" -- "$@") if [[ $? -ne 0 ]]; then echo "getopt ha fallito il parsing" exit 2 fi eval set -- "$PARSED" d=n f=n v=0 outFile=- while true; case $1 in ) ;; ) ;; ) ;; --) shift break ;; *) echo "È stato riscontrato un errore" exit 3 ;; esac done # handle non-option arguments if [[ $# -ne 1 ]]; then echo "$0: A single input file is required." exit 4 fi echo "verbose: $v, force: $f, debug: $d, in: $1, out: $outFile"
Considerazioni sull'esempio d'uso
L'istruzione
eval set -- "$PARSED"
sostituisce i parametri posizionali passati allo script con la stringa PARSED, che è la loro versione canonica. 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.
getopts
Pro
A differenza di getopt, il comando getopts è un built-in della shell. Questo comporta che:
- è 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;
- In quanto built-in, può avere accesso diretto alle variabili della shell, che può usare per fare il parsing.
Contro
È in grado di 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 getopts 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 opzionale. Di default getopts fa il parsing dei parametri posizionali, cioè della stringa $@; se è presente una stringa args, farà il parsing dei parametri contenuti in quest'ultima. |
Come visto nella tabella, il comando getopts viene 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. Per le opzioni non sono consentiti i caratteri ':' e '?'.
Se l'opzione prevede un parametro ad essa associato, il carattere associato all'opzione in optstring va fatto seguire dal carattere ':'. Quindi, se si vuole aggiungere l'opzione 'f' e a questa si vuole associare un parametro, nella stringa optstring si aggiungerà la sequenza 'f:'
Il comando getopts può essere impostato nella modalità silenziosa, aggiungendo il carattere ':' come suffisso di optstring. Se questo carattere è assente, getopts è in modalità verbosa. La differenza consiste nel fatto che nel secondo caso produrrà dei messaggi qualora rilevasse degli errori 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.
Se, ad esempio, volessimo che getopts 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 |
Viene impostata al valore dell'argomento relativo a ogni parametro trovato da getopts. 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 getopts 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 usato all'interno di un ciclo while. Ad ogni iterazione del ciclo, legge un'opzione, e l'eventuale parametro associato, dalla stringa dei parametri posizionali forniti allo script (o da args, se fornita); se tutto è corretto, aggiorna le sue variabili interne, secondo quanto visto al punto precedente, e fornisce questi dati all'utente per la loro analisi, generalmente all'interno di un costrutto case. Il comando restituisce un exit status 1 quando non ci sono più parametri da leggere, facendo terminare il ciclo. Inoltre, il comando interrompe il parsing se incontra la sequenza di caratteri '--'.
Modalità verbosa
#!/usr/bin/env bash while getopts "vg:uf:" option; do case $option in v ) echo "Passata l'opzione: -$option" echo "Non ha un parametro associato, quindi OPTARG=$OPTARG" echo "Il valore di OPTIND è $OPTIND" echo "*** *** ***" ;; g ) echo "Passata l'opzione: -$option" echo "Ha un parametro associato, quindi OPTARG=$OPTARG" echo "Il valore di OPTIND è $OPTIND" echo "*** *** ***" ;; \?) echo "Termino" exit 1 ;; * ) echo "Opzione non ancora implementata: -$option" echo "*** *** ***" ;; esac done shift $((OPTIND -1)) echo "Rimangono da analizzare i parametri: $@"
Alcuni esempi dell'output generato da questo script sono:
Modalità sileziosa
#!/usr/bin/env bash while getopts ":vg:uf:" option; do case $option in v ) echo "Passata l'opzione: -$option" echo "Non ha un parametro associato, quindi OPTARG=$OPTARG" echo "Il valore di OPTIND è $OPTIND" echo "*** *** ***" ;; g ) echo "Passata l'opzione: -$option" echo "Ha un parametro associato, quindi OPTARG=$OPTARG" echo "Il valore di OPTIND è $OPTIND" echo "*** *** ***" ;; \?) echo "Opzione non valida: -$OPTARG" >&2 echo "Il valore di OPTIND è $OPTIND" >&2 echo "*** *** ***" exit 1 ;; : ) echo "L'opzione: -$OPTARG richede un parametro, che non è stato passato" >&2 echo "Il valore di OPTIND è $OPTIND" >&2 echo "*** *** ***" exit 1 ;; * ) echo "Opzione non ancora implementata: -$option" >&2 exit 1 esac done shift $((OPTIND -1)) echo "Rimangono da analizzare i parametri: $@"
Alcuni esempi dell'output generato da questo script sono:
Considerazioni sugli esempi d'uso
- In entrambe le modalità, lo script si aspetta di trovare, tra i parametri posizionali, le opzioni 'v' e 'u' senza parametro associato e le opzioni 'g' ed 'f' con un parametro associato.
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 però continua il parsing. 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 'u' ed 'f'.
L'istruzione shift successiva al ciclo while ha lo scopo di eliminare dalla stringa dei parametri posizionali quelli che sono già stati analizzati con getopts. In questo modo, lo script è in grado di gestire successivamente anche eventuali ulteriori parametri posizionali.