Successivo: Programma egrep, Su: Cloni [Contenuti][Indice]
Il programma di utilità cut seleziona, o “taglia” (cut),
caratteri o campi dal suo standard input e li
spedisce al suo standard output.
I campi sono separati da caratteri TAB per default,
ma è possibile fornire un’opzione dalla riga di comando per cambiare il campo
delimitatore (cioè, il carattere che separa i campi). La definizione di
campo di cut è meno generale di quella di awk.
Un uso comune del comando cut potrebbe essere quello di estrarre
i nomi degli utenti correntemente collegati al sistema, a partire
dall’output del comando who. Per esempio, la seguente
pipeline genera una lista in ordine alfabetico, senza doppioni, degli utenti
correntemente collegati al sistema:
who | cut -c1-8 | sort | uniq
Le opzioni per cut sono:
-c listaUsare lista come lista di caratteri da ritagliare. Elementi all’interno della lista possono essere separati da virgole, e intervalli di caratteri possono essere separated da trattini. La lista ‘1-8,15,22-35’ specifica i caratteri da 1 a 8, 15, e da 22 a 35.
-f listaUsare lista come lista di campi da ritagliare.
-d delimitatoreUsare delimitatore come carattere che separa i campi invece del carattere TAB.
-sEvita la stampa di righe che non contengono il delimitatore di campo.
L’implementazione awk del comando cut usa la funzione
di libreria getopt()
(vedi la sezione Elaborare opzioni specificate sulla riga di comando)
e la funzione di libreria join()
(vedi la sezione Trasformare un vettore in una sola stringa).
Il programma inizia con un commento che descrive le opzioni, le funzioni
di libreria necessarie, e una funzione sintassi() che stampa un
messaggio ed esce. sintassi() è chiamato se si specificano degli
argomenti non validi:
# cut.awk --- implementa cut in awk # Opzioni: # -f lista Ritagliare campi # -d c Carattere di delimitazione di campo # -c lista Ritagliare caratteri # # -s Sopprimere righe che non contengono il delimitatore # # Richiede le funzioni di libreria getopt() e join()
function sintassi()
{
print("sintassi: cut [-f lista] [-d c] [-s] [file...]") > "/dev/stderr"
print("sintassi: cut [-c lista] [file...]") > "/dev/stderr"
exit 1
}
Subito dopo c’è una regola BEGIN che analizza le opzioni della riga
di comando.
Questa regola imposta FS a un solo carattere TAB, perché quello è
il separatore di campo di cut per default.
La regola poi imposta il separatore di campo in output allo stesso valore
del separatore di campo in input. Un ciclo che usa getopt() esamina
le opzioni della riga di comando. Una e una sola delle variabili
per_campi o per_caratteri è impostata a "vero", per indicare
che l’elaborazione sarà fatta per campi o per caratteri, rispettivamente.
Quando si ritaglia per caratteri, il separatore di campo in output è
impostato alla stringa nulla:
BEGIN {
FS = "\t" # default
OFS = FS
while ((c = getopt(ARGC, ARGV, "sf:c:d:")) != -1) {
if (c == "f") {
per_campi = 1
lista_campi = Optarg
} else if (c == "c") {
per_caratteri = 1
lista_campi = Optarg
OFS = ""
} else if (c == "d") {
if (length(Optarg) > 1) {
printf("cut: usa il primo carattere di %s" \
" come delimitatore\n", Optarg) > "/dev/stderr"
Optarg = substr(Optarg, 1, 1)
}
fs = FS = Optarg
OFS = FS
if (FS == " ") # mette specifica in formato awk
FS = "[ ]"
} else if (c == "s")
sopprimi = 1
else
sintassi()
}
# Toglie opzioni da riga di comando
for (i = 1; i < Optind; i++)
ARGV[i] = ""
Nella scrittura del codice si deve porre particolare attenzione quando il
delimitatore di campo è uno spazio. Usare
un semplice spazio (" ") come valore per FS è
sbagliato: awk separerebbe i campi con serie di spazi,
TAB, e/o ritorni a capo, mentre devono essere separati solo da uno spazio.
Per far questo, salviamo il carattere di spazio originale nella variabile
fs per un uso futuro; dopo aver impostato FS a "[ ]" non
è possibile usarlo direttamente per vedere se il carattere delimitatore di
campo è nella stringa.
Si ricordi anche che dopo che si è finito di usare getopt()
(come descritto nella Elaborare opzioni specificate sulla riga di comando),
è necessario
eliminare tutti gli elementi del vettore ARGV da 1 a Optind,
in modo che awk non tenti di elaborare le opzioni della riga di comando
come nomi-file.
Dopo aver elaborato le opzioni della riga di comando, il programma verifica
che le opzioni siano coerenti. Solo una tra le opzioni -c
e -f dovrebbe essere presente, ed entrambe richiedono una lista di
campi. Poi il programma chiama
prepara_lista_campi() oppure prepara_lista_caratteri() per
preparare la lista dei campi o dei caratteri:
if (per_campi && per_caratteri)
sintassi()
if (per_campi == 0 && per_caratteri == 0)
per_campi = 1 # default
if (lista_campi == "") {
print "cut: specificare lista per -c o -f" > "/dev/stderr"
exit 1
}
if (per_campi)
prepara_lista_campi()
else
prepara_lista_caratteri()
}
prepara_lista_campi() pone la lista campi, usando la virgola come
separatore, in un vettore. Poi, per
ogni elemento del vettore, controlla che esso non sia un intervallo. Se è
un intervallo, lo fa diventare un elenco. La funzione controlla l’intervallo
specificato, per assicurarsi che il primo numero sia minore del secondo.
Ogni numero nella lista è aggiunto al vettore lista_c, che
semplicemente elenca i campi che saranno stampati. Viene usata la normale
separazione in campi di awk. Il programma lascia ad awk
il compito di separare i campi:
function prepara_lista_campi( n, m, i, j, k, f, g)
{
n = split(lista_campi, f, ",")
j = 1 # indice in lista_c
for (i = 1; i <= n; i++) {
if (index(f[i], "-") != 0) { # un intervallo
m = split(f[i], g, "-")
if (m != 2 || g[1] >= g[2]) {
printf("cut: lista campi errata: %s\n",
f[i]) > "/dev/stderr"
exit 1
}
for (k = g[1]; k <= g[2]; k++)
lista_c[j++] = k
} else
lista_c[j++] = f[i]
}
ncampi = j - 1
}
La funzione prepara_lista_caratteri() è più complicata di
prepara_lista_campi().
L’idea qui è di usare la variabile di gawk FIELDWIDTHS
(vedi la sezione Leggere campi di larghezza costante),
che descrive input a larghezza costante. Quando si usa una lista di
caratteri questo è proprio il nostro caso.
Impostare FIELDWIDTHS è più complicato che semplicemente elencare
i campi da stampare. Si deve tener traccia dei campi da
stampare e anche dei caratteri che li separano, che vanno saltati.
Per esempio, supponiamo che si vogliano i caratteri da 1 a 8, 15,
e da 22 a 35. Per questo si specifica ‘-c 1-8,15,22-35’. Il valore che
corrisponde a questo nella variabile FIELDWIDTHS è
"8 6 1 6 14". Questi sono cinque campi, e quelli da stampare
sono $1, $3, e $5.
I campi intermedi sono riempitivo (filler),
ossia è ciò che separa i dati che si desidera estrarre.
lista_c lista i campi da stampare, e t traccia l’elenco
completo dei campi, inclusi i riempitivi:
function prepara_lista_caratteri( campo, i, j, f, g, n, m, t,
filler, ultimo, lungo)
{
campo = 1 # contatore totale campi
n = split(lista_campi, f, ",")
j = 1 # indice in lista_c
for (i = 1; i <= n; i++) {
if (index(f[i], "-") != 0) { # intervallo
m = split(f[i], g, "-")
if (m != 2 || g[1] >= g[2]) {
printf("cut: lista caratteri errata: %s\n",
f[i]) > "/dev/stderr"
exit 1
}
lungo = g[2] - g[1] + 1
if (g[1] > 1) # calcola lunghezza del riempitivo
filler = g[1] - ultimo - 1
else
filler = 0
if (filler)
t[campo++] = filler
t[campo++] = lungo # lunghezza del campo
ultimo = g[2]
lista_c[j++] = campo - 1
} else {
if (f[i] > 1)
filler = f[i] - ultimo - 1
else
filler = 0
if (filler)
t[campo++] = filler
t[campo++] = 1
ultimo = f[i]
lista_c[j++] = campo - 1
}
}
FIELDWIDTHS = join(t, 1, campo - 1)
ncampi = j - 1
}
Poi viene la regola che elabora i dati. Se l’opzione -s è stata
specificata, il flag sopprimi
è vero. La prima istruzione
if accerta che il record in input abbia il separatore di
campo. Se cut sta elaborando dei campi, e sopprimi è vero,
e il carattere di separazione dei campi non è presente nel record, il
record è ignorato.
Se il record è valido, gawk ha già separato i dati in campi,
usando il carattere in FS o usando campi a lunghezza fissa
e FIELDWIDTHS. Il ciclo scorre attraverso la lista di campi che
si dovrebbero stampare. Il campo corrispondente è stampato se contiene dati.
Se il campo successivo contiene pure dei dati, il carattere di separazione è
scritto tra i due campi:
{
if (per_campi && sopprimi && index($0, fs) == 0)
next
for (i = 1; i <= ncampi; i++) {
if ($lista_c[i] != "") {
printf "%s", $lista_c[i]
if (i < ncampi && $lista_c[i+1] != "")
printf "%s", OFS
}
}
print ""
}
Questa versione di cut utilizza la variabile FIELDWIDTHS di
gawk per ritagliare in base alla posizione dei caratteri. È
possibile, in altre implementazioni di awk usare substr()
(vedi la sezione Funzioni di manipolazione di stringhe), ma
la cosa è molto più complessa.
La variabile FIELDWIDTHS fornisce una soluzione elegante al problema
di suddividere la riga in input in singoli caratteri.
Successivo: Programma egrep, Su: Cloni [Contenuti][Indice]