Grep, Sed, Gawk e altri utili filtri

Loris Tissino

15 luglio 2009

Filtri

I programmi che funzionano come filtro agiscono facendo delle modifiche a quello che leggono dal proprio standard input e generando di conseguenza uno standard output.

Alcuni esempi semplici:

Cut

Il programma cut "preleva" alcuni campi delle righe di input.

$ getent passwd root | cut -d: -f7
/bin/bash

Expand e Unexpand

Il programma expand converte i caratteri di tabulazione in spazi. Il programma unexpand fa l'opposto.

$ cat bashscript
#!/bin/bash
for i in $@
        do
                echo $i
        done
$ cat bashscript | expand -t4
#!/bin/bash
for i in $@
    do
        echo $i
    done
$ cat bashscript | expand -t1
#!/bin/bash
for i in $@
 do
  echo $i
 done

Fmt

Il programma fmt formatta il testo a una specifica lunghezza della riga.

$ cat testolungo.txt
Un filtro software è un programma che trasforma dati in base a criteri predeterminati.
Tra i programmi di tipo filtro, esistono dei software in grado di selezionare pagine su Internet in base ad alcuni     
[snip]
(dalla Wikipedia)
$ cat testolungo.txt | fmt 
Un filtro software è un programma che trasforma dati in base a
criteri predeterminati.  Tra i programmi di tipo filtro, esistono dei
software in grado di selezionare pagine su Internet in base ad alcuni
[snip]
giochi, ecc.).  (dalla Wikipedia)
$ cat testolungo.txt| fmt -w 40
Un filtro software è un programma
che trasforma dati in base a criteri
predeterminati.  Tra i programmi di
[snip]
(dalla Wikipedia)

Head e Tail

I programmi head e tail mostrano, rispettivamente, le prime e le ultime righe (o i primi e gli ultimi byte, a seconda dell'opzione) di un file.

Se si indica di leggere più di un file, viene mostrata una riga di intestazione per ciascun file.

$ head -n2 /etc/services /etc/protocols 
==> /etc/services <==
# Network services, Internet style
#

==> /etc/protocols <==
# Internet (IP) protocols
#
$ tail -n2 /etc/services /etc/protocols 
==> /etc/services <==
fido            60179/tcp                       # fidonet EMSI over TCP
# Local services

==> /etc/protocols <==
sctp    132     SCTP            # Stream Control Transmission Protocol
fc      133     FC              # Fibre Channel

Notare che l'opzione abbreviata -2 funziona con head ma non con tail.

Il programma tail può funzionare anche per seguire in tempo reale la crescita di un file (utile per i log).

Join

Il programma join stampa una linea per ciascuna coppia di linee provenienti da due file con un campo in comune (con lo stesso valore), un po' come con un join tra tabelle in una base di dati.

$ cat tipi-mime
image/gif                          gif
image/jpeg                         jpeg jpg jpe
image/png                          png
image/tiff                         tiff tif
image/x-ms-bmp                     bmp
image/x-photoshop                  psd
$ cat lista-defaults
image/bmp eog
image/gif eog
image/jpeg eog
image/jpg eog
image/png eog
image/tiff gimp-2.2
image/x-psd gimp-2.2
$ join tipi-mime lista-defaults
image/gif gif eog
image/jpeg jpeg jpg jpe eog
image/png png eog
image/tiff tiff tif gimp-2.2

Importante: i due file devono essere ordinati rispetto ai campi di join.

Nl

Il programma nl numera le righe dei file di input tenendo in considerazione l'eventuale presenza di stringhe speciali (es. \:) per la delimitazione di intestazione, corpo e piè di pagina.

$ cat testo.txt 
\:\:\:
I filtri
\:\:
Un filtro software è un
programma che trasforma
dati in base a criteri
predeterminati.
\:
Filtro (wikipedia)
$ nl testo.txt

       I filtri

     1  Un filtro software è un
     2  programma che trasforma
     3  dati in base a criteri
     4  predeterminati.

       Filtro (wikipedia)

Od

Il programma od mostra il contenuto di un file in ottale, esadecimale e in vari altri formati.

$ cat testoacapo 
testo
a
capo
$ od -t a testoacapo 
0000000   t   e   s   t   o  nl   a  nl   c   a   p   o  nl
0000015
$ od -t c testoacapo 
0000000   t   e   s   t   o  \n   a  \n   c   a   p   o  \n
0000015
$ od -t x1 testoacapo 
0000000 74 65 73 74 6f 0a 61 0a 63 61 70 6f 0a
0000015
$ od -t x4 testoacapo 
0000000 74736574 0a610a6f 6f706163 0000000a
0000015

Paste

Il programma paste accosta le linee corrispondenti di uno o più file.

$ cat nomi 
mario
giovanni
luisa
$ cat cognomi 
rossi
bianchi
neri
$ paste nomi cognomi 
mario   rossi
giovanni        bianchi
luisa   neri
$ paste -d' ' nomi cognomi 
mario rossi
giovanni bianchi
luisa neri
$ paste -s -d\; nomi cognomi 
mario;giovanni;luisa
rossi;bianchi;neri

Pr

Il programma pr converte il file di input in una versione impaginata, con intestazioni e piè di pagina.

$ cat testolungo.txt | fmt -w 30 | pr -l 12 -W 30  -h "Documento sui filtri software"

Uniq e Wc

Il programma uniq elimina righe duplicate adiacenti; per questo motivo è spesso usato dopo sort.

Il programma wc conta i caratteri, le parole e le righe.

$ getent passwd | cut -d: -f7 | sort | uniq  # quali sono le shell impostate
/bin/bash
/bin/false
/bin/sh
/bin/sync
/usr/sbin/nologin

$ getent passwd | cut -d: -f7 | sort | uniq  | wc -l # quante sono le shell impostate
5

Grep

Il programma grep mantiene le righe che soddisfano (o non soddisfano) un certo criterio costituito da una espressione regolare.

$ getent services | grep http
www                   80/tcp http
https                 443/tcp
https                 443/udp

getent services | grep ^http
https                 443/tcp
https                 443/udp

$ getent services | head -5 | grep -v udp
tcpmux                1/tcp
echo                  7/tcp
discard               9/tcp sink null

Con grep è anche possibile contare le righe in cui c'è la corrispondenza, mostrare le righe adiacenti, ecc.

Gawk

Il programma gawk agisce sui singoli campi, riformattandoli, usandoli per fare delle operazioni, ecc.

Per i prossimi esempi, immaginiamo di avere un file di testo chiamato datipersonali. Innanzitutto diamo un'occhiata al suo contenuto:

$ cat datipersonali
12 alice pordenone
24 berto udine
36 camilla trieste
48 daniele gorizia
60 elisabetta treviso

Gawk, estrazione di campi

Con gawk possiamo estrarre i singoli campi:

$ cat datipersonali | gawk '{ print $3 }'
pordenone
udine
trieste
gorizia
treviso
$ cat datipersonali | gawk '{ print $2 }'
alice
berto
camilla
daniele
elisabetta

Gawk, estrazione di campi (2)

Con gawk possiamo estrarre i singoli campi:

$ cat datipersonali | gawk '{print $0}'  # tutti i campi
12 alice pordenone
24 berto udine
36 camilla trieste
48 daniele gorizia
60 elisabetta treviso
$ cat datipersonali | gawk 'BEGIN {n=3} {print $n}'
pordenone
udine
trieste
gorizia
treviso

Gawk, test sui campi

Con gawk possiamo fare dei test sul valore dei singoli campi:

$ cat datipersonali | gawk '{ if ($2 == "alice") print $3 }'
pordenone
$ cat datipersonali | gawk '{ if ($1 <= 40) print $2 }'
alice
berto
camilla

Gawk, operazione sui valori dei campi

Con gawk possiamo fare operazioni sui valori dei singoli campi:

$ cat datipersonali | gawk '{s += $1} END {print s}'
180
$ cat datipersonali | gawk 'BEGIN {s=10000} {s += $1} END {print s}'
10180

Gawk, separatore

Possiamo indicare ciò che consideriamo separatore dei campi:

$ getent passwd | gawk 'BEGIN {FS=":"} {print $1}' | tail -5
andrea_p
federica_p
marco_p
monica_s
mattia_z

Gawk, espressioni regolari

Possiamo lavorare solo sulle righe che contengono una determinata espressione regolare:

$ cat datipersonali | gawk '/ab/ {print $3}'
treviso

... oppure controllare la corrispondenza di un singolo campo:

$ cat datipersonali | gawk '{if ($2 ~ /a/) print $3}'
pordenone
trieste
gorizia
treviso
$ cat datipersonali | gawk '{if ($2 !~ /a/) print $3}'
udine

Gawk, controllo del tipo di file

Si supponga di voler trovare tutti i file di un certo tipo (restituito dal comando file) in una directory e in tutte le sue sottodirectory. Ad esempio, tutti i file di tipo text della directory /sbin.

Una pipeline risolve il problema:

$ find /sbin -type f| xargs -i file {} | gawk \
    '{ FS=":"; if ($2 ~ /text/) print $1}'
/sbin/MAKEDEV
/sbin/insmod_ksymoops_clean
/sbin/kernelversion
/sbin/shadowconfig
/sbin/fsck.nfs
/sbin/hotplug
/sbin/mdrun
/sbin/update-modules.modutils
/sbin/update-modules
...

Gawk, riformattazione

I campi dell'input possono essere ricalcolati/riformattati.

Ad esempio, il log di squid presenta righe in cui nel primo campo c'è il numero di secondi dall'epoch, ma potremmo desiderare dei valori più comprensibili:

$ cat squid_access.log
1173783723.856    337 192.168.3.2 TCP_MISS/2...
1173783723.908    561 192.168.3.2 TCP_MISS/2...
1173783724.113   1952 192.168.3.2 TCP_MISS/2...

$ cat squid_access.log | gawk '{$1=strftime("%F_%T",$1); print $0}'
2007-03-13_12:02:03 337 192.168.3.2 TCP_MISS/2...
2007-03-13_12:02:03 561 192.168.3.2 TCP_MISS/2...
2007-03-13_12:02:04 1952 192.168.3.2 TCP_MISS/2...

Sed, lo stream editor

Sed agisce sulle righe dell'input in diversi modi.

L'uso più comune ha a che fare con la sostituzione di stringhe:

$ getent services | head -3 | sed 's/echo/ecco/'
tcpmux                1/tcp
ecco                  7/tcp
ecco                  7/udp

... ma anche:

$ getent services | head -3 | sed 'sa/a|a'
tcpmux                1|tcp
echo                  7|tcp
echo                  7|udp

Sed, selezione di gruppi di righe

Sed può selezionare gruppi di righe.

In base al contenuto:

$ getent services| sort | sed -n '/^fido/,/^ftp/p'
fido                  60179/tcp
finger                79/tcp
font-service          7100/tcp xfs
font-service          7100/udp xfs
frox                  2121/tcp
fsp                   21/udp fspd
ftp                   21/tcp

... oppure in base al numero di riga:

$ getent services| sort | cat -n | sed -n 12,18p
    12  afs3-callback         7001/udp
    13  afs3-errors           7006/tcp
    14  afs3-errors           7006/udp
    15  afs3-fileserver       7000/tcp bbs
    16  afs3-fileserver       7000/udp bbs
    17  afs3-kaserver         7004/tcp
    18  afs3-kaserver         7004/udp