Successivo: Funzioni Passwd, Precedente: Gestione File Dati, Su: Funzioni di libreria [Contenuti][Indice]
La maggior parte dei programmi di utilità su sistemi compatibili con POSIX
prevedono opzioni presenti sulla riga di comando che possono essere usate per
cambiare il modo in cui un programma si comporta. awk
è un esempio di
tali programmi (vedi Opzioni).
Spesso le opzioni hanno degli argomenti (cioè, dati che servono al
programma per eseguire correttamente le opzioni specificate
sulla riga di comando).
Per esempio, l’opzione -F di awk
richiede di usare la stringa
specificata
come separatore di campo. La prima occorrenza, sulla riga di comando, di
-- o di una stringa che non inizia con ‘-’ segnala la fine
delle opzioni.
I moderni sistemi Unix hanno una funzione C chiamata getopt()
per
elaborare gli argomenti presenti
sulla riga di comando. Il programmatore fornisce una
stringa che descrive le opzioni, ognuna delle quali consiste di
una sola lettera. Se un’opzione richiede un
argomento, nella stringa l’opzione è seguita da due punti.
A getopt()
vengono anche
passati il numero e i valori degli argomenti presenti sulla riga di comando
e viene chiamata in un ciclo.
getopt()
scandisce gli argomenti della riga di comando cercando
le lettere delle opzioni.
A ogni passaggio del ciclo restituisce un carattere
singolo che rappresenta la successiva lettera di opzione trovata, o ‘?’
se viene trovata un’opzione non prevista.
Quando restituisce -1, non ci sono ulteriori
opzioni da trattare sulla riga di comando.
Quando si usa getopt()
, le opzioni che non prevedono argomenti
possono essere raggruppate.
Inoltre, le opzioni che hanno argomenti richiedono obbligatoriamente che
l’argomento sia specificato.
L’argomento può seguire immediatamente la lettera
dell’opzione, o può costituire un argomento separato sulla riga di comando.
Dato un ipotetico programma che ha tre opzioni sulla riga di comando, -a, -b e -c, dove -b richiede un argomento, tutti i seguenti sono modi validi per invocare il programma:
programma -a -b pippo -c dati1 dati2 dati3 programma -ac -bpippo -- dati1 dati2 dati3 programma -acbpippo dati1 dati2 dati3
Si noti che quando l’argomento è raggruppato con la sua opzione, la parte rimanente dell’argomento è considerato come argomento dell’opzione. In quest’esempio, -acbpippo indica che tutte le opzioni -a, -b e -c sono presenti, e che ‘pippo’ è l’argomento dell’opzione -b.
getopt()
fornisce quattro variabili esterne a disposizione del
programmatore:
optind
L’indice nel vettore dei valori degli argomenti (argv
) dove si può
trovare il primo argomento sulla riga di comando che non sia un’opzione.
optarg
Il valore (di tipo stringa) dell’argomento di un’opzione.
opterr
Solitamente getopt()
stampa un messaggio di errore quando trova un’opzione
non valida. Impostando opterr
a zero si disabilita questa funzionalità.
(un’applicazione potrebbe voler stampare un proprio messaggio di errore.)
optopt
La lettera che rappresenta l’opzione sulla riga di comando.
Il seguente frammento di codice C mostra come getopt()
potrebbe
elaborare gli argomenti della riga di comando per awk
:
int main(int argc, char *argv[]) { … /* stampa un appropriato messaggio */ opterr = 0; while ((c = getopt(argc, argv, "v:f:F:W:")) != -1) { switch (c) { case 'f': /* file */ … break; case 'F': /* separatore di campo */ … break; case 'v': /* assegnamento di variabile */ … break; case 'W': /* estensione */ … break; case '?': default: messaggio_di_aiuto(); break; } } … }
Incidentalmente, gawk
al suo interno usa la funzione GNU
getopt_long()
per elaborare sia le normali opzioni che quelle lunghe
in stile GNU
(vedi Opzioni).
L’astrazione fornita da getopt()
è molto utile ed è piuttosto comoda
anche nei programmi awk
. Di seguito si riporta una versione
awk
di getopt()
. Questa funzione mette in evidenza uno dei
maggiori punti deboli di awk
, che è quello di essere molto carente
nella manipolazione di caratteri singoli. Sono necessarie ripetute chiamate a
substr()
per accedere a caratteri singoli.
(vedi Funzioni per stringhe).74
La spiegazione della funzione viene data man mano che si elencano i pezzi di codice che la compongono:
# getopt.awk --- imita in awk la funzione di libreria C getopt(3) # Variabili esterne: # Optind -- indice in ARGV del primo argomento che non è un'opzione # Optarg -- valore di tipo stringa dell'argomento dell'opzione corrente # Opterr -- se diverso da zero, viene stampato un messaggio diagnostico # Optopt -- lettera dell'opzione corrente # Restituisce: # -1 alla fine delle opzioni # "?" per un'opzione non riconosciuta # <c> un carattere che rappresenta l'opzione corrente # Dati privati: # _opti -- indice in un'opzione multipla, p.es., -abc
La funzione inizia con commenti che elencano e descrivono le variabili globali utilizzate, spiegano quali sono i valori di ritorno, il loro significato, e ogni altra variabile che è “esclusiva” a questa funzione di libreria. Tale documentazione è essenziale per qualsiasi programma, e in modo particolare per le funzioni di libreria.
La funzione getopt()
dapprima controlla che sia stata effettivamente
chiamata con una stringa di opzioni (il parametro opzioni
). Se
opzioni
ha lunghezza zero, getopt()
restituisce immediatamente
-1:
function getopt(argc, argv, opzioni, unaopz, i) { if (length(opzioni) == 0) # nessuna opzione specificata return -1
if (argv[Optind] == "--") { # fatto tutto Optind++ _opti = 0 return -1
} else if (argv[Optind] !~ /^-[^:[:space:]]/) { _opti = 0 return -1 }
Il successivo controllo cerca la fine delle opzioni. Due trattini
(--) marcano la fine delle opzioni da riga di comando, e lo stesso
fa qualsiasi
argomento sulla riga di comando che non inizi con ‘-’. Optind
è
usato per scorrere il vettore degli argomenti presenti sulla riga di comando;
mantiene il suo valore attraverso chiamate successive a getopt()
, perché
è una variabile globale.
L’espressione regolare che viene usata, /^-[^:[:space:]/
,
chiede di cercare un
‘-’ seguito da qualsiasi cosa che non sia uno spazio vuoto o un carattere
di due punti. Se l’argomento corrente sulla riga di comando non corrisponde a
quest’espressione regolare, vuol dire che non si tratta di un’opzione, e
quindi viene terminata l’elaborazione delle opzioni. Continuando:
if (_opti == 0) _opti = 2 unaopz = substr(argv[Optind], _opti, 1) Optopt = unaopz i = index(opzioni, unaopz) if (i == 0) { if (Opterr) printf("%c -- opzione non ammessa\n", unaopz) > "/dev/stderr" if (_opti >= length(argv[Optind])) { Optind++ _opti = 0 } else _opti++ return "?" }
La variabile _opti
tiene traccia della posizione nell’argomento
della riga di comando correntemente in esame
(argv[Optind]
). Se opzioni multiple sono
raggruppate con un ‘-’ (p.es., -abx), è necessario
restituirle all’utente una per volta.
Se _opti
è uguale a zero, viene impostato a due, ossia all’indice
nella
stringa del successivo carattere da esaminare (‘-’, che è alla
posizione uno viene ignorato).
La variabile unaopz
contiene il carattere,
ottenuto con substr()
. Questo è salvato in Optopt
per essere
usato dal programma principale.
Se unaopz
non è nella stringa delle opzioni opzioni
,
si tratta di un’opzione
non valida. Se Opterr
è diverso da zero, getopt()
stampa un
messaggio di errore sullo standard error che è simile al messaggio
emesso dalla versione C di getopt()
.
Poiché l’opzione non è valida, è necessario tralasciarla e passare al successivo
carattere di opzione. Se _opti
è maggiore o uguale alla lunghezza
dell’argomento corrente della riga di comando, è necessario passare al
successivo argomento, in modo che Optind
venga incrementato e
_opti
sia reimpostato a zero. In caso contrario, Optind
viene
lasciato com’è e _opti
viene soltanto incrementato.
In ogni caso, poiché l’opzione non è valida, getopt()
restituisce
"?"
. Il programma principale può esaminare Optopt
se serve
conoscere quale lettera di opzione è quella non valida. Proseguendo:
if (substr(opzioni, i + 1, 1) == ":") { # ottiene un argomento di opzione if (length(substr(argv[Optind], _opti + 1)) > 0) Optarg = substr(argv[Optind], _opti + 1) else Optarg = argv[++Optind] _opti = 0 } else Optarg = ""
Se l’opzione richiede un argomento, la lettera di opzione è seguita da due punti
nella stringa opzioni
. Se rimangono altri caratteri nell’argomento
corrente sulla riga di comando (argv[Optind]
), il resto di quella stringa
viene assegnato a Optarg
. Altrimenti, viene usato il successivo
argomento sulla riga di comando (‘-xFOO’ piuttosto che
‘-x FOO’). In
entrambi i casi, _opti
viene reimpostato a zero, perché non ci sono altri
caratteri da esaminare nell’argomento corrente sulla riga di comando.
Continuando:
if (_opti == 0 || _opti >= length(argv[Optind])) { Optind++ _opti = 0 } else _opti++ return unaopz }
Infine, se _opti
è zero o maggiore della lunghezza dell’argomento
corrente sulla riga di comando, significa che l’elaborazione di
quest’elemento in argv
è
terminata, quindi Optind
è incrementato per
puntare al successivo elemento in argv
. Se nessuna delle condizioni è
vera, viene incrementato solo _opti
, cosicché la successiva lettera di
opzione può essere elaborata con la successiva chiamata a getopt()
.
La regola BEGIN
inizializza sia Opterr
che Optind
a uno.
Opterr
viene impostato a uno, perché il comportamento di default per
getopt()
è quello di stampare un messaggio diagnostico dopo aver visto
un’opzione non valida. Optind
è impostato a uno, perché non
c’è alcun motivo
per considerare il nome del programma, che è in ARGV[0]
:
BEGIN { Opterr = 1 # il default è eseguire una diagnosi Optind = 1 # salta ARGV[0] # programma di controllo if (_getopt_test) { while ((_go_c = getopt(ARGC, ARGV, "ab:cd")) != -1) printf("c = <%c>, Optarg = <%s>\n", _go_c, Optarg) printf("argomenti che non sono opzioni:\n") for (; Optind < ARGC; Optind++) printf("\tARGV[%d] = <%s>\n", Optind, ARGV[Optind]) } }
Il resto della regola BEGIN
è un semplice programma di controllo. Qui
sotto si riportano i risultati di
due esecuzioni di prova
del programma di controllo:
$ awk -f getopt.awk -v _getopt_test=1 -- -a -cbARG bax -x -| c = <a>, Optarg = <> -| c = <c>, Optarg = <> -| c = <b>, Optarg = <ARG> -| argomenti che non sono opzioni: -| ARGV[3] = <bax> -| ARGV[4] = <-x> $ awk -f getopt.awk -v _getopt_test=1 -- -a -x -- xyz abc -| c = <a>, Optarg = <> error→ x -- opzione non ammessa -| c = <?>, Optarg = <> -| argomenti che non sono opzioni: -| ARGV[4] = <xyz> -| ARGV[5] = <abc>
In entrambe le esecuzioni, il primo -- fa terminare gli argomenti dati
ad awk
, in modo che awk
non tenti di interpretare le opzioni
-a, etc. come sue opzioni.
NOTA: Dopo che
getopt()
è terminato, il codice a livello utente deve eliminare tutti gli elementi diARGV
da 1 aOptind
, in modo cheawk
non tenti di elaborare le opzioni sulla riga di comando come nomi-file.
Usare ‘#!’ con l’opzione -E può essere d’aiuto per evitare
conflitti tra le opzioni del proprio programma e quelle di gawk
,
poiché l’opzione -E fa sì che gawk
abbandoni
l’elaborazione di ulteriori opzioni.
(vedi Script eseguibili e
vedi Opzioni).
Molti degli esempi presentati in
Programmi di esempio,
usano getopt()
per elaborare i propri argomenti.
Questa funzione
è stata scritta prima che gawk
acquisisse la capacità di
dividere le stringhe in caratteri singoli usando ""
come separatore.
È stata lasciata così, poiché l’uso di substr()
è più portabile.
Successivo: Funzioni Passwd, Precedente: Gestione File Dati, Su: Funzioni di libreria [Contenuti][Indice]