Tutti i sistemi operativi basati su Unix forniscono almeno tre diversi canali input e output – chiamati rispettivamente stdin, stdout e stderr – che permettono la comunicazione fra un programma e l’ambiente in cui esso viene eseguito.
In Bash ciascuno di questi canali viene numerato da 0 a 2, e prende il nome di file descriptor poiché fa riferimento ad un particolare file: così come avviene con un qualunque altro file memorizzato nel sistema, è possibile manipolarlo, copiarlo, leggerlo o scrivere su di esso. Bash offre anche la possibilità di aprirne di nuovi se le esigenze lo richiedono (utilizzando i numeri successivi al 2).
Quando un ambiente Bash viene avviato, tutti e tre i file descriptor di default puntano al terminale nel quale è stata inizializzata la sessione:
l’input (stdin – 0) corrisponde a ciò che viene digitato nel terminale, ed entrambi gli output
– stdout (1) per i messaggi tradizionali e stderr (2) per quelli di errore – vengono inviati al terminale. Infatti, un terminale aperto in un sistema operativo basato su Unix, è in genere esso stesso un file, comunemente memorizzato in /dev/tty0; quando viene aperta una nuova sessione in parallelo ad una esistente, il nuovo terminale sarà /dev/tty1 e così via.
Perciò, inizialmente i tre file descriptor puntano tutti al file che rappresenta il terminale in cui vengono eseguiti.
L’operatore > si utilizza per effettuare il redirect dell’output. Eseguendo:
comando >file
Bash tenta prima di aprire il file con i permessi di scrittura e – nel caso questa operazione abbia successo – invia lo stream stdout al file appena aperto (sovrascrivendone il contenuto esistente). Lo stesso effetto si ottiene attraverso il comando:
comando 1>file
Il motivo è che stdout è il file descriptor numero 1. In generale è possibile scrivere comando n>file per redirezionare il file descriptor n verso file. Nel caso in cui si desideri preservare il contenuto del file e far sì che l’output stream gli venga concatenato, l’operatore da utilizzare diventa >>.
È anche possibile redirezionare sia stdout che stderr verso uno stesso file utilizzando l’operatore &> (o equivalentemente >&), cioè scrivendo:
comando &> file
Lo stesso effetto si ottiene redirezionando i due stream in sequenza, come ad esempio:
comando >file 2>&1
L’output stream viene redirezionato verso file e successivamente stderr viene redirezionato verso l’output stream, cioè file. Dato che i comandi vengono valutati sequenzialmente l’ordine in cui si specificano i redirect è significativo, perciò scrivere:
comando >file 2>&1
Non è equivalente a scrivere comando 2>&1 >file: dopo aver eseguito quest’ultimo, solo stdout sarà redirezionato verso file, poichè stderr è stato redirezionato verso la destinazione di default dell’output stream (/dev/tty0) prima che questa venisse cambiata.
Quando un comando è troppo verboso, non si è interessati al suo output o lo si vuole nascondere all’utente, è possibile redirezionare stdout o stderr (o entrambi) verso lo speciale nodo /dev/null, il cui contenuto viene sempre scartato dal sistema.

Come è facile immaginare, la redirezione di un canale di input (come quello predefinito stdin) avviene utilizzando l’operatore <: volendo dare un file in input ad un comando o ad uno script, si utilizza quindi la notazione seguente:
comando <file
Ad esempio, se vogliamo leggere il contenuto della prima riga di un file e assegnarlo alla variabile riga, è possibile semplicemente eseguire:
read -r riga < file
Infine, nel caso dei canali di input, l’operatore << seguito da un marker impone alla shell di leggere l’input da stdin fintantoché non incontra il marker; a quel punto, Bash trasmette l’intero contenuto dell’input letto fino a quel momento al canale stdin del comando che lo riceve.
Tutte le redirezioni dei canali viste finora sono state effettuate per un singolo comando; ma come si fa a stabilire una redirezione permanente dei canali I/O da un certo momento in avanti?
Tramite il comando exec:
exec 2>file
comando1
comando2
…
Nell’esempio appena riportato, lo stream stderr viene redirezionato verso file dal comando exec 2>file; da quel momento in avanti lo stream stderr relativo ai comandi comando1, comando2 e tutti i successivi verrà scritto nel file specificato.
Un’alternativa per effettuare il redirect soltanto temporaneo di comandi multipli verso uno stesso file si ottiene sequenzializzandoli mediante l’operatore ;:
(comando1 ; comando2) >file
Il codice precedente redireziona lo standard output di ciascun comando all’interno delle parentesi verso il file specificato.
Un altro operatore molto utile per la manipolazione degli stream I/O è quello di pipelining: |. Tramite questo simbolo, una sequenza di comandi separata da esso fa sì che ciascun comando venga eseguito allo stesso momento e che l’output di ognuno venga passato come input a quello successivo, da sinistra verso destra. Dobbiamo però fare attenzione: ogni comando viene eseguito in una subshell, perciò ogni variabile modificata da ciascuno non sarà poi letta dagli altri comandi della sequenza o nell’ambiente in cui viene eseguita l’intera pipeline, come si nota eseguendo il codice seguente:
echo “Prova” | (read var ; echo $var)
Per concludere, un comando molto utile – non fornito da Bash di default ma presente in pressoché tutti i sistemi basati su Unix – è tee: esso reindirizza lo stream input sia verso stdout sia verso un file specificato. Eseguire:
echo messaggio | tee file
stamperà messaggio a schermo e nel file specificato come argomento.