Ogni processo Linux nasce con tre canali di comunicazione già aperti, e la shell può riassegnarli prima di lanciare il comando. Capito questo, la redirezione smette di essere una collezione di simboli da memorizzare e diventa una cosa sola: cambiare l’estremo di un canale. All’esame però i simboli vanno anche scritti, quindi conviene fissarne la forma esatta.
Tre canali, tre numeri
I tre canali standard sono lo standard input (stdin), lo standard output (stdout) e lo standard error (stderr). A ciascuno corrisponde un file descriptor, cioè un numero: 0 per stdin, 1 per stdout, 2 per stderr. In una sessione normale stdin è la tastiera, mentre stdout e stderr sono entrambi lo schermo — motivo per cui a occhio nudo sembrano lo stesso canale, ma non lo sono. Sono raggiungibili anche come file speciali: /dev/stdin, /dev/stdout, /dev/stderr.
La separazione fra 1 e 2 esiste proprio perché tu possa trattarli in modo diverso: i messaggi utili da una parte, gli errori e la diagnostica dall’altra.
Mandare l’output su file: uno, l’altro o entrambi
Il segno > seguito da un percorso riassegna lo stdout a quel file. È l’abbreviazione di 1>: quando il numero manca, la shell assume 1. Per catturare gli errori serve invece 2>. Il file viene sovrascritto senza chiedere nulla; con >> (e 2>>) i dati vengono aggiunti in coda, e il file viene creato se non esiste.
$ cat /proc/cpuinfo > /tmp/cpu.txt
$ cat /proc/cpu_info 2> /tmp/errori.txt
$ cat /proc/cpu_info 2>> /tmp/errori.txt
Per portare un canale dentro l’altro si usa la e commerciale davanti al numero: 2>&1 significa “manda stderr dove sta andando adesso stdout”. Da qui la forma canonica per salvare tutto in un file solo, comando > log.txt 2>&1, e la sua scorciatoia bash comando &> log.txt.
⚠️ L’ordine conta. In comando > log.txt 2>&1 prima stdout va nel file, poi stderr lo segue: nel file finisce tutto. In comando 2>&1 > log.txt stderr viene agganciato allo stdout di quel momento, che è ancora lo schermo, e solo dopo stdout viene dirottato sul file: gli errori restano a video. È una domanda d’esame frequente.
⚠️ Portabilità. Su Debian e Ubuntu /bin/sh è dash, sulle famiglie Red Hat è un collegamento a bash. In uno script con shebang /bin/sh la forma &> non è affidabile: usa > file 2>&1, che funziona ovunque. Nota anche che il solo modo per far leggere gli errori a un altro programma è prima fonderli con lo stdout.
Per buttare via l’output c’è /dev/null, scrivibile da chiunque e senza fondo: comando > /dev/null 2>&1 esegue in silenzio totale.
noclobber: impedire alla shell di sovrascrivere
Dato che > distrugge il contenuto precedente senza avvisare, bash offre un’opzione di protezione: set -o noclobber, in forma breve set -C. Con noclobber attivo una redirezione verso un file esistente fallisce con cannot overwrite existing file.
$ set -o noclobber
$ date > /tmp/esistente.txt
-bash: /tmp/esistente.txt: cannot overwrite existing file
$ date >| /tmp/esistente.txt
$ set +o noclobber
Tre cose da ricordare: si disattiva con set +C o set +o noclobber; >| forza la sovrascrittura anche a protezione attiva; e l’append con >> non è mai bloccato, come non lo è la scrittura su /dev/null, che è un file speciale. Per rendere l’impostazione permanente va messa nel profilo bash dell’utente o in quello di sistema.
Leggere: da un file, da un blocco di testo, da una stringa
La redirezione di input fa scorrere i dati da destra a sinistra: il segno di minore prende un percorso e lo attacca allo stdin del comando, che è il descriptor 0 sottinteso. Serve con i programmi che leggono solo dallo standard input.
Ci sono poi due varianti che non toccano il filesystem. Il here document, due segni di minore seguiti da una parola qualsiasi, apre un inserimento multiriga che finisce quando quella parola compare da sola su una riga. Il here string, tre segni di minore, manda allo stdin una singola stringa.
$ uniq -c < /tmp/errori.txt
$ wc -c <<EOF
Quanti caratteri
in questo here document?
EOF
$ bc <<< "scale=6; 1/3"
Fra i segni e la parola terminatrice non ci vanno spazi. Se scrivi la parola terminatrice fra apici singoli la shell non espande variabili né comandi dentro il blocco: utile quando il testo contiene simboli di dollaro che vuoi lasciare intatti.
Oltre il 2
I numeri 0, 1 e 2 sono solo quelli aperti per te: un programma può usare qualsiasi intero superiore come canale proprio, e la shell glielo può assegnare con la stessa grammatica, per esempio 3 seguito dal segno di minore e da un file per leggere, oppure 4>&1 per scrivere sullo stdout. Per vedere quali file un processo ha davvero aperto guarda i collegamenti simbolici in /proc/PID/fd/:
$ ls -l /proc/1234/fd
È il modo più rapido per scoprire dove un servizio sta scrivendo i suoi log quando la configurazione non te lo dice.