Salta al contenuto principale

cut: estrai colonne e campi da un testo

Inviato da tuxsa il

cut ritaglia da ogni riga di un testo solo le parti che ti interessano: una colonna di un file CSV, il nome utente di /etc/passwd, i primi caratteri di ogni riga. È uno degli strumenti più usati nelle pipeline, in coda a comandi come grep o ls e prima di sort e uniq.

Cos'è

cut significa "taglia": come le forbici su un foglio, ritaglia una porzione di ogni riga. Fa parte del pacchetto GNU coreutils ed è un eseguibile (/usr/bin/cut), presente su qualsiasi distribuzione GNU/Linux.

Cosa fa

Legge uno o più file, o lo standard input, e per ogni riga stampa solo le parti richieste, che possono essere:

  • dei campi (-f), cioè pezzi separati da un carattere delimitatore, come le colonne di un CSV;
  • dei caratteri (-c) in posizioni fisse, contando dall'inizio della riga;
  • dei byte (-b).

Non modifica nulla nei file di partenza e non riordina né elabora il contenuto: taglia e basta.

Come si usa

$ cut [opzioni] [file...]

Si sceglie sempre una modalità (-f, -c o -b) seguita da un elenco di posizioni, scritto così:

ElencoSignificato
3Solo il terzo elemento
1,3Il primo e il terzo
2-4Dal secondo al quarto
2-Dal secondo fino alla fine
-3Dall'inizio fino al terzo
1,3-5Combinazioni di quanto sopra

La numerazione parte da 1, non da 0. Per gli esempi usiamo il file clienti.csv:

$ cat clienti.csv
nome,città,età
Mario,Salerno,42
Anna,Napoli,35
Luca,Avellino,28
Giulia,Salerno,31

Esempio 1: estrarre una colonna

$ cut -d, -f1 clienti.csv
nome
Mario
Anna
Luca
Giulia

Con -d, (delimiter) si indica il carattere che separa i campi, qui la virgola; con -f1 (fields) si chiede il primo campo. Non serve lasciare uno spazio tra -d e il carattere.

Esempio 2: più colonne, intervalli e resto della riga

$ cut -d, -f1,3 clienti.csv
nome,età
Mario,42
Anna,35
Luca,28
Giulia,31
$ cut -d, -f2- clienti.csv
città,età
Salerno,42
Napoli,35
Avellino,28
Salerno,31

Con -f1,3 si prendono il primo e il terzo campo; con -f2- dal secondo in poi. L'output usa lo stesso delimitatore dell'ingresso.

Esempio 3: tutto tranne un campo, con --complement

$ cut -d, -f2 --complement clienti.csv
nome,età
Mario,42
...

L'opzione --complement (versione GNU) inverte la scelta: stampa tutto tranne i campi indicati.

Esempio 4: cambiare il delimitatore di uscita

$ cut -d, -f1-3 --output-delimiter=';' clienti.csv
nome;città;età
Mario;Salerno;42
Anna;Napoli;35
Luca;Avellino;28
Giulia;Salerno;31

Con --output-delimiter si sceglie il separatore dei campi in uscita. Qui il CSV con le virgole diventa un CSV con il punto e virgola, il formato che la versione italiana di Excel e di LibreOffice si aspetta.

Esempio 5: i file di sistema, separati da due punti

$ cut -d: -f1,7 /etc/passwd | head -n 3
root:/bin/bash
daemon:/usr/sbin/nologin
bin:/usr/sbin/nologin

Nel file /etc/passwd ogni riga descrive un utente con campi separati da due punti: il primo è il nome, il settimo la shell di accesso. È l'esempio classico con cui si impara cut.

Esempio 6: i primi caratteri di ogni riga con -c

$ echo "Salerno" | cut -c1-3
Sal
$ echo "20261015" | cut -c1-4
2026
$ echo "20261015" | cut -c5-6
10
$ ls -l | cut -c1-10
totale 8
-rw-r--r--
drwxr-xr-x

Con -c si lavora per posizione, utile con testi a larghezza fissa. L'ultimo esempio mostra solo i permessi, cioè i primi 10 caratteri di ls -l, come visto nell'articolo su chmod.

Esempio 7: contare quante volte compare un valore

$ tail -n +2 clienti.csv | cut -d, -f2 | sort | uniq -c | sort -nr
      2 Salerno
      1 Napoli
      1 Avellino

La catena parte da tail -n +2, che salta la riga di intestazione, estrae la colonna con cut, la ordina e conta le ripetizioni con sort | uniq -c e ordina la classifica. È lo schema già visto negli articoli su head e tail e su sort e uniq.

Esempio 8: gli indirizzi più frequenti in un log

$ cut -d' ' -f1 /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -n 3
   1204 66.249.66.1
    388 93.184.216.34
    251 151.101.1.69

Nel registro degli accessi di nginx, nel formato predefinito, il primo campo, separato da uno spazio, è l'indirizzo IP del visitatore. Con questa riga si ottiene la classifica degli indirizzi che hanno fatto più richieste (gli indirizzi qui sono d'esempio).

Esempio 9: i domini di un elenco di indirizzi e-mail

$ cut -d@ -f2 iscritti.txt | sort | uniq -c | sort -nr

Qui il delimitatore è la chiocciola: il secondo campo di ogni indirizzo è il dominio.

Esempio 10: l'ultimo campo con rev

$ echo "archivio.tar.gz" | rev | cut -d. -f1 | rev
gz
$ echo "/home/enzo/Documenti/relazione.pdf" | rev | cut -d/ -f1 | rev
relazione.pdf

Il comando cut non sa contare dalla fine ("l'ultimo campo"). Un trucco è invertire ogni riga con rev, tagliare il primo campo e invertire di nuovo. Per i percorsi esistono anche basename e dirname.

Esempio 11: colonne separate da più spazi

Molti comandi (df, ps, ls -l) allineano le colonne con più spazi. Per cut ogni spazio è un delimitatore, quindi compaiono campi vuoti. Si risolve comprimendo prima gli spazi con tr -s:

$ df -h | tail -n +2 | tr -s ' ' | cut -d' ' -f1,5
tmpfs 1%
/dev/sda2 54%
tmpfs 0%
/dev/sda1 2%

Con tr -s ' ' ogni sequenza di spazi diventa uno spazio solo; tail -n +2 toglie l'intestazione. L'articolo su df mostra le stesse colonne: qui si estraggono dispositivo e percentuale di uso. Per questi casi awk è più comodo, perché tratta già le sequenze di spazi come un unico separatore.

Esempio 12: il delimitatore di tabulazione

$ cut -f2 dati.tsv
$ cut -d $'\t' -f2 dati.tsv

Se non indichi -d, il delimitatore predefinito è la tabulazione. Per scriverla esplicitamente in Bash si usa $'\t'.

Esempio 13: uso negli script

#!/bin/bash
CSV="${1:?Indica il file CSV}"

echo "Città e numero di clienti:"
tail -n +2 "$CSV" | cut -d, -f2 | sort | uniq -c | sort -nr

Lo script si lancia come ./citta.sh clienti.csv e, con il file d'esempio, stampa la stessa classifica dell'esempio 7. La costruzione ${1:?messaggio} interrompe lo script con un errore se manca l'argomento. Quando i campi da estrarre sono molti, in Bash è più chiaro leggerli in variabili:

while IFS=, read -r nome citta eta; do
    echo "$nome abita a $citta"
done < <(tail -n +2 clienti.csv)

Opzioni utili

  • -d carattere: delimitatore dei campi (un solo carattere; predefinito: tabulazione).
  • -f elenco: seleziona i campi.
  • -c elenco: seleziona i caratteri per posizione.
  • -b elenco: seleziona i byte per posizione.
  • -s: con -f, salta le righe che non contengono il delimitatore.
  • --complement: seleziona tutto tranne ciò che è indicato (GNU).
  • --output-delimiter=testo: delimitatore da usare in uscita (GNU).
  • -z: separa le righe con il carattere nullo invece che con l'a capo (GNU).

Attenzione a

  • Il delimitatore è un solo carattere: cut -d', ' -f1 dà l'errore il delimitatore deve essere un singolo carattere. Per separatori di più caratteri usa awk o sed.
  • Le righe senza delimitatore vengono stampate intere, ed è un comportamento che sorprende:

    $ printf 'a:b\nsenza\nc:d\n' | cut -d: -f2
    b
    senza
    d
    $ printf 'a:b\nsenza\nc:d\n' | cut -d: -f2 -s
    b
    d

    Con -s quelle righe vengono scartate.

  • Gli spazi multipli sono tanti delimitatori: due spazi consecutivi creano un campo vuoto in mezzo. Comprimili prima con tr -s ' ' oppure usa awk. Anche i nomi con spazi, in colonne come quelle di ls -l, spezzano i campi.
  • cut non riordina i campi: cut -d, -f3,1 stampa comunque prima il campo 1 e poi il 3, nell'ordine in cui compaiono. Per invertire l'ordine serve awk.
  • CSV complessi: cut non conosce le virgolette. Una riga come "Rossi, Mario",Salerno verrebbe spezzata anche dentro il nome. Per i CSV reali servono strumenti dedicati (per esempio csvkit o un linguaggio di scripting).
  • I caratteri accentati con -c: nelle versioni GNU più diffuse, -c funziona come -b e conta i byte, non le lettere. In UTF-8 una "à" occupa due byte, quindi echo "città" | cut -c1-4 può tagliare a metà la lettera e produrre un carattere rovinato (cit�). Con testi accentati usa i campi (-f) oppure awk o sed.
  • I file di Windows: se il file termina le righe con \r\n, l'ultimo campo si porta dietro un \r invisibile, e il confronto con altri valori sembra non funzionare. Lo vedi con cat -A e lo togli con tr -d '\r'.
  • Righe con meno campi del previsto: se chiedi il campo 5 e la riga ne ha 3, cut stampa una riga vuota, senza segnalare errori. Controlla i dati con awk -F, '{print NF}' prima di fidarti dei risultati.
  • Numerazione da 1: -f0 è un errore.
  • Non è un editor: cut scrive il risultato a schermo. Per salvarlo, reindirizza su un altro file; non scrivere sullo stesso file di ingresso, con >, perché lo svuoteresti.

Riepilogo

ComandoCosa fa
cut -d, -f1 filePrimo campo, separato da virgole
cut -d: -f1,7 filePrimo e settimo campo, separati da due punti
cut -d, -f2- fileDal secondo campo in poi
cut -d, -f2 --complement fileTutti i campi tranne il secondo
cut -d, -f1-3 --output-delimiter=';' fileCambia il separatore in uscita
cut -c1-3 filePrimi tre caratteri di ogni riga
cut -f2 fileSecondo campo, separato da tabulazioni
cut -d: -f2 -s fileIgnora le righe senza delimitatore
… | tr -s ' ' | cut -d' ' -f2Colonne separate da più spazi
… | rev | cut -d. -f1 | revUltimo campo di una riga