Successivo: Programma sed semplice, Precedente: Programma riordino diario, Su: Programmi vari [Contenuti][Indice]
Sia questo capitolo che il precedente
(Funzioni di libreria)
presentano un numero elevato di programmi awk
.
Se si vuole fare pratica con questi programmi, è fastidioso doverli
digitare di nuovo manualmente. È per questo che abbiamo pensato a un programma
in grado di estrarre parti di un file in input Texinfo e metterli in file
separati.
Questo Documento è scritto in Texinfo, il programma di formattazione di documenti del progetto GNU. Un solo file sorgente Texinfo può essere usato per produrre sia la documentazione stampata, usando TeX, sia quella online. (Texinfo è esaurientemente documentato nel libro Texinfo—The GNU Documentation Format, disponibile alla Free Software Foundation, e anche online.)
Per quel che ci riguarda, è sufficiente sapere tre cose riguardo ai file di input Texinfo:
awk
. I simboli ‘@’ sono rappresentati nel sorgente
Texinfo come ‘@@’.
Il programma seguente, extract.awk, legge un file sorgente Texinfo
e fa due cose, basandosi sui commenti speciali.
Dopo aver visto il commento ‘@c system …’,
esegue un comando, usando il testo del comando contenuto nella
riga di controllo e passandolo alla funzione system()
(vedi Funzioni di I/O).
Dopo aver trovato il commento ‘@c file nome_file’, ogni riga
successiva è spedita al file nome_file, fino a che si trova un
commento ‘@c endfile’.
Le regole in extract.awk sono soddisfatte sia quando incontrano
‘@c’ che quando incontrano ‘@comment’ e quindi la parte
‘omment’ è opzionale.
Le righe che contengono ‘@group’ e ‘@end group’ sono semplicemente
ignorate.
extract.awk usa la funzione di libreria join()
(vedi Funzione join).
I programmi di esempio nel sorgente Texinfo online di GAWK: Programmare efficacemente in AWK
(gawktexi.in) sono stati tutti inseriti tra righe ‘file’ e righe
‘endfile’. La distribuzione di gawk
usa una copia di
extract.awk per estrarre i programmi di esempio e per installarne
molti in una particolare directory dove gawk
li può trovare.
Il file Texinfo ha un aspetto simile a questo:
… Questo programma ha una regola @code{BEGIN} che stampa un messaggio scherzoso: @example @c file esempi/messages.awk BEGIN @{ print "Non v'allarmate!" @} @c endfile @end example Stampa anche qualche avviso conclusivo: @example @c file esempi/messages.awk END @{ print "Evitate sempre gli archeologi annoiati!" @} @c endfile @end example …
Il programma extract.awk inizia con l’impostare IGNORECASE
a
uno, in modo che un miscuglio di lettere maiuscole e minuscole nelle direttive
non faccia differenza.
La prima regola gestisce le chiamate a system()
, controllando che sia
stato fornito un comando (NF
dev’essere almeno tre) e controllando
anche che il comando termini con un codice di ritorno uguale a zero, che sta
a significare che tutto è andato bene:
# extract.awk --- estrae file ed esegue programmi dal file Texinfo BEGIN { IGNORECASE = 1 } /^@c(omment)?[ \t]+system/ { if (NF < 3) { e = ("extract: " FILENAME ":" FNR) e = (e ": riga `system' con formato errato") print e > "/dev/stderr" next } $1 = "" $2 = "" stat = system($0) if (stat != 0) { e = ("extract: " FILENAME ":" FNR) e = (e ": attenzione: system ha restituito " stat) print e > "/dev/stderr" } }
La variabile e
è stata usata per far sì che la regola
sia agevolemente contenuta nella videata.
La seconda regola gestisce il trasferimento di dati in un file. Verifica che nella direttiva sia stato fornito un nome-file. Se il nome del file non è quello del file corrente, il file corrente viene chiuso. Mantenere aperto il file corrente finché non si trova un nuovo nome file permette di usare la ridirezione ‘>’ per stampare i contenuti nel file, semplificando la gestione dei file aperti.
Il ciclo for
esegue il lavoro. Legge le righe usando getline
(vedi Getline).
Se si raggiunge una fine-file inattesa, viene chiamata la funzione
fine_file_inattesa()
. Se la riga è una riga “endfile”,
il ciclo viene abbandonato.
Se la riga inizia con ‘@group’ o ‘@end group’, la riga viene
ignorata, e si passa a quella seguente. Allo stesso modo, eventuali commenti
all’interno degli esempi vengono ignorati.
Il grosso del lavoro è nelle poche righe che seguono. Se la riga non ha
simboli ‘@’, il programma la può
stampare così com’è. Altrimenti, ogni ‘@’ a inizio parola dev’essere
eliminato.
Per rimuovere i simboli ‘@’, la riga viene divisa nei singoli elementi
del vettore a
, usando la funzione split()
(vedi Funzioni per stringhe).
Il simbolo ‘@’ è usato come carattere di separazione.
Ogni elemento del vettore a
che risulti vuoto indica due caratteri
‘@’ contigui nella riga originale. Per ogni due elementi vuoti
(‘@@’ nel file originale), va inserito un solo simbolo ‘@’ nel
file in output.
Una volta terminato di esaminare il vettore, viene chiamata la funzione join()
specificando nella chiamata il valore di SUBSEP
(vedi Vettori multidimensionali),
per riunire nuovamente i pezzi in una riga sola.
La riga è poi stampata nel file di output:
/^@c(omment)?[ \t]+file/ { if (NF != 3) { e = ("extract: " FILENAME ":" FNR ": riga `file' con formato errato") print e > "/dev/stderr" next } if ($3 != file_corrente) { if (file_corrente != "") close(file_corrente) file_corrente = $3 } for (;;) { if ((getline riga) <= 0) fine_file_inattesa() if (riga ~ /^@c(omment)?[ \t]+endfile/) break else if (riga ~ /^@(end[ \t]+)?group/) continue else if (riga ~ /^@c(omment+)?[ \t]+/) continue if (index(riga, "@") == 0) { print riga > file_corrente continue } n = split(riga, a, "@") # if a[1] == "", vuol dire riga che inizia per @, # non salvare un @ for (i = 2; i <= n; i++) { if (a[i] == "") { # era un @@ a[i] = "@" if (a[i+1] == "") i++ } } print join(a, 1, n, SUBSEP) > file_corrente } }
È importante notare l’uso della ridirezione ‘>’ . L’output fatto usando ‘>’ apre il file solo la prima volta; il file resta poi aperto, e ogni scrittura successiva è aggiunta in fondo al file. (vedi Ridirezione). Ciò rende possibile mischiare testo del programm e commenti esplicativi (come è stato fatto qui) nello stesso file sorgente, senza nessun problema. Il file viene chiuso solo quando viene trovato un nuovo nome di file-dati oppure alla fine del file in input.
Per finire, la funzione fine_file_inattesa()
stampa un
appropriato messaggio di errore ed esce.
La regola END
gestisce la pulizia finale, chiudendo il file aperto:
function fine_file_inattesa() { printf("extract: %s:%d: fine-file inattesa, o errore\n", FILENAME, FNR) > "/dev/stderr" exit 1 }
END { if (file_corrente) close(file_corrente) }
Successivo: Programma sed semplice, Precedente: Programma riordino diario, Su: Programmi vari [Contenuti][Indice]