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 è:
ls -a -l --color=auto file (o /percorso/al/file)
Il comando è composto da una serie di opzioni chiamate argomenti.
Il primo argomento è il nome del comando, mentre i successivi sono indicati col nome di parametri posizionali.
Questi ultimi possono essere suddivisi in alcune categorie logiche:
opzione corta: consiste in un trattino seguito da una singola lettera (ad esempio, -a nel comando precedente), esse possono essere raggruppate insieme (ad esempio, -al nel comando precedente).
opzione lunga: possono essere in due formati, in quello Gnu consiste in due trattini seguito dal nome completo dell'opzione, mentre in quello XF86 ha un singolo trattino seguito dal nome completo dell'opzione.
parametro: specifica quale azione deve svolgere un'opzione, di regola accoppiato ad un'opzione lunga preceduto da un uguale (ad esempio , =auto nel comando precedente)
file: consiste nel nome del file su cui agisce il comando, completo del percorso alla cartella in cui è allocato se differente a quella in cui si è (ad esempio, pippo.txt se nella stessa cartella, /home/topolinia/amici/pippo se in cartella differente).
Il comando precedente si può dattilografare anche così:
ls --color=auto -la percorso
in cui, tutte le opzioni corte sono state riunite ed è stato cambiato l'ordine di tutte le 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 glibc (cioè, la GNU lib c). Ciononostante, può essere facilmente installata nelle distribuzioni in cui non sia presente di default.
Inoltre, il comandogetopt --test
restituisce un exit status 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
getopt può essere chiamato con tre diverse sintassi. La più semplice è:
getopt optstring parameters
La stringa optsting 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.
parameters è la stringa degli 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.
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; le parti racchiuse tra parentesi quadre sono opzionali; 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. 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. 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 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 -l "help,file:,dir::" -- "hf:d::" "$@"
Ora, getopt accetta anche le opzioni nella versione lunga: 'help' senza parametro, 'file' con un parametro obbligatorio e 'dir' con un parametro facoltativo.
getopt -l "help,file:,dir::" -o "hf:d::" -a -- "$@"
Qui getopt accetta le stesse opzioni del caso precedente, ma queste possono essere 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 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 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. 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 'a-lunga', 'b-lunga' e 'c-lunga' nello stile Gnu (con due trattini come prefisso).
#! /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 viene mostrata una lista di differenti modi di chiamarlo e alcuni dei rispettivi output prodotti:
$ ./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', ma interpretato come argomento a sé stante ed elencato inseme a 'foo' tra gli argomenti rimanenti dopo il parsing.$ ./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, mentre l'argomento '5' viene associato all'opzione '-b'. Visto che per questa opzione il parametro è obbligatorio, si può usare la sintassi equivalente '-b5'.- I due esempi precedenti possono essere sintetizzati nel comando:
$ ./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, come 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'
- Volendo usare la versione lunga dell'opzione con parametro obbligatorio, le due sintassi equivalenti sarebbero
$ ./prova_getopt.sh --b-lunga 5
e$ ./prova_getopt.sh --b-lunga=5
- Per l'opzione corta '-c', che accetta un parametro opzionale, l'unica sintassi valida è quella in cui opzione e parametro sono scritti attaccati
$ ./prova_getopt.sh -c6
- Nel caso dell'opzione lunga '--c-lunga', opzione e parametro sono separati dal carattere '='
$ ./prova_getopt.sh --c-lunga=6
Considerazioni sull'esempio d'uso
- 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 riga
echo "Versione canonica dei parametri prodotta da getopt: "$TEMP" "
visualizza la riorganizzazione dei parametri nella versione canonica prodotta da getopt. Ai fini del funzionamento dello script è superflua.
L'istruzione
eval set -- "$TEMP"
sostituisce i parametri posizionali passati allo script con la stringa TEMP, 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 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.