Hai già ricevuto alcune ottime risposte. Vorrei sottolineare tuttavia che ci sono due diversi concetti coinvolti qui, la cui comprensione aiuta enormemente:
Sfondo: descrittore di file e tabella di file
Il descrittore di file è solo un numero 0 ... n, che è l'indice nella tabella dei descrittori di file nel processo. Per convenzione, STDIN = 0, STDOUT = 1, STDERR = 2 (nota che i termini STDIN
ecc. Qui sono solo simboli / macro usati dalla convenzione in alcuni linguaggi di programmazione e pagine man, non esiste un vero "oggetto" chiamato STDIN; per lo scopo di questa discussione, STDIN è 0, ecc.).
La tabella descrittore di file in sé non contiene alcuna informazione su quale sia il file effettivo. Al contrario, contiene un puntatore a una tabella di file diversa; quest'ultimo contiene informazioni su un file fisico reale (o dispositivo a blocchi, pipe o qualsiasi altra cosa che Linux possa indirizzare tramite il meccanismo dei file) e ulteriori informazioni (vale a dire, sia che si tratti di leggere o scrivere).
Quindi quando usi >
o <
nella tua shell, sostituisci semplicemente il puntatore del rispettivo descrittore di file per indicare qualcos'altro. La sintassi 2>&1
indica semplicemente il descrittore 2 ovunque 1 punti. > file.txt
si apre semplicemente file.txt
per la scrittura e consente a STDOUT (decsriptor file 1) di indicarlo.
Ci sono altri gadget, ad esempio 2>(xxx)
(es: creare un nuovo processo in esecuzione xxx
, creare una pipe, collegare il descrittore di file 0 del nuovo processo all'estremità di lettura della pipe e collegare il descrittore di file 2 del processo originale alla fine di scrittura del tubo).
Questa è anche la base del "file handle magic" in altri software oltre alla shell. Ad esempio, è possibile, nello script Perl, dup
licenziare il descrittore di file STDOUT in un altro (temporaneo), quindi riaprire STDOUT in un file temporaneo appena creato. Da questo punto in poi, tutto l'output di STDOUT dal tuo script Perl e tutte le system()
chiamate di quello script finiranno in quel file temporaneo. Al termine, puoi riportare il dup
tuo STDOUT al descrittore temporaneo in cui lo hai salvato e presto tutto sarà come prima. Puoi anche scrivere a quel descrittore temporaneo nel frattempo, quindi mentre l'output STDOUT effettivo passa al file temporaneo, puoi comunque effettivamente output roba sul vero STDOUT (comunemente, l'utente).
Risposta
Per applicare le informazioni di base fornite sopra alla domanda:
In quale ordine la shell esegue i comandi e reindirizza i flussi?
Da sinistra a destra.
<command> > file.txt 2>&1
fork
fuori da un nuovo processo.
- Apri
file.txt
e memorizza il suo puntatore nel descrittore di file 1 (STDOUT).
- Punta STDERR (descrittore di file 2) su qualunque cosa la fd 1 indichi in questo momento (che di nuovo è
file.txt
ovviamente già aperto ).
exec
il <command>
Questo apparentemente reindirizza prima stderr a stdout, quindi lo stdout risultante viene reindirizzato a file.txt.
Ciò avrebbe senso se ci fosse una sola tabella, ma come spiegato sopra ce ne sono due. I descrittori di file non si indicano reciprocamente in modo ricorsivo, non ha senso pensare "reindirizzare STDERR a STDOUT". Il pensiero corretto è "puntare STDERR ovunque punti STDOUT". Se cambi STDOUT in un secondo momento, STDERR rimane dove si trova, non si adatta magicamente a ulteriori modifiche a STDOUT.