2012-07-19 8 views
10

Quando si combina lo stderr con lo stdout, perché 2>&1 deve venire prima di un | (pipe) ma dopo un > myfile (reindirizzamento al file)?Perché 2> & 1 devono venire prima di un | (pipe) ma dopo un "> myfile" (reindirizza al file)?

Per reindirizzare stderr allo stdout per l'output di file:

echo > myfile 2>&1 

Per reindirizzare stderr allo stdout per un tubo:

echo 2>&1 | less 



La mia ipotesi è che ho potuto solo fare:

echo | less 2>&1 

e così via d lavoro, ma non è così. Perchè no?

risposta

16

Una tubazione è un elenco limitato di comandi. Qualsiasi reindirizzamento specificato si applica ai comandi costitutivi (semplici o composti), ma non alla pipeline nel suo complesso. Ogni pipe incatena lo stdout di un comando allo stdin del successivo applicando implicitamente un reindirizzamento a ciascuna subshell prima di, vengono valutati tutti i reindirizzamenti associati a un comando.

cmd 2>&1 | less 

Prima stdout del primo sottoshell viene reindirizzato al tubo da cui less sta leggendo. Successivamente, il reindirizzamento 2>&1 viene applicato al primo comando. Il reindirizzamento dello stderr allo stdout funziona perché lo stdout sta già puntando alla pipe.

cmd | less 2>&1 

Qui, il reindirizzamento applica a less. Lo stdout e lo stderr di Less sono presumibilmente iniziati puntati al terminale, quindi 2>&1 in questo caso non ha alcun effetto.

Se si desidera un reindirizzamento da applicare a un intero gasdotto, per raggruppare più comandi come parte di una conduttura, o alle condotte nido, quindi utilizzare un gruppo di comando (o di qualsiasi altro comando composto):

{ { cmd1 >&3; cmd2; } 2>&1 | cmd3; } 3>&2 

Potrebbe essere un tipico esempio. Il risultato finale è: cmd1 e cmd2's stderr ->cmd3; cmd2 stdout ->cmd3; e cmd1 e cmd3 s sterr e lo stdout cmd3 -> il terminale.

Se si utilizza la pipe |& specifica di Bash, le cose si fanno strane, perché ogni reindirizzamento stdout della pipeline si verifica ancora per primo, ma il reindirizzamento stderr viene effettivamente per ultimo. Quindi, ad esempio:

f() { echo out; echo err >&2; }; f >/dev/null |& cat 

Ora, contrariamente a ciò, l'output è nascosto. Il primo stdout di f passa alla pipe, il successivo stdout di f viene reindirizzato a /dev/null e infine, stderr viene reindirizzato allo stdout (/dev/null ancora).

Io raccomando di non usare mai |& in Bash - è qui usato per la dimostrazione.

+3

+1 spiegato bene – jordanm

+2

+1. L'unica cosa che aggiungerei è che un pipe è un separatore di comandi, come punto e virgola. –

+1

+1 Wow, ottima risposta @ormaaj! Esattamente quello che stavo cercando - grazie! –

6

Per aggiungere alla risposta di ormaaj:

Il motivo è necessario specificare gli operatori di reindirizzamento nel giusto ordine è che sono valutati da sinistra a destra. Considera questi elenchi di comandi:

# print "hello" on stdout and "world" on stderr 
{ echo hello; echo world >&2; } 

# Redirect stdout to the file "out" 
# Then redirect stderr to the file "err" 
{ echo hello; echo world >&2; } > out 2> err 

# Redirect stdout to the file "out" 
# Then redirect stderr to the (already redirected) stdout 
# Result: all output is stored in "out" 
{ echo hello; echo world >&2; } > out 2>&1 

# Redirect stderr to the current stdout 
# Then redirect stdout to the file "out" 
# Result: "world" is displayed, and "hello" is stored in "out" 
{ echo hello; echo world >&2; } 2>&1 > out 
2

La mia risposta è comprendendo i descrittori di file. Ogni processo ha una serie di descrittori di file: le voci ai file che vengono aperti. Di default, il numero 0 è per lo stdin, il numero 1 è per lo stdout e il numero 2 per lo stderr.

I/o redirector> e < per impostazione predefinita si collegano ai descrittori di file più ragionevoli, stout e stdin. Se instradi lo stdout in un file (come con foo > bar), all'avvio del processo 'pippo', il file 'bar' viene aperto per la scrittura e collegato al descrittore di file numero 1. Se vuoi solo stderr in 'bar', 'd utilizzare foo 2> bar che apre la barra di file e lo aggancia allo stderr.

Ora l'i/o redirector '2> & 1'. Normalmente leggo che come 'metti il ​​descrittore di file 2 come il descrittore di file 1. Mentre leggi la riga di comando da sinistra a destra, puoi fare il seguente: foo 1>bar 2>&1 1>/dev/tty. Con questo, il descrittore di file 1 è impostato sul file 'bar', il descrittore di file 2 è impostato sullo stesso di 1 (quindi 'bar') e dopo di ciò, il descrittore di file 1 è impostato su/dev/tty. Il runnning foo invia il suo output a/dev/tty e lo stderr al file 'bar'.

Ora arriva la pipeline: ciò non modifica i descrittori di file, tuttavia li collegherà tra i processi: stdout del processo di sinistra di stdin del successivo. Stderr è passato. Quindi, se ti piace che la pipeline funzioni solo su stderr, devi usare foo 2| bar, che collega lo stderr di foo allo stdin di bar. (Non sono sicuro di ciò che accade con lo stdout di foo.)

Con quanto sopra, se si utilizza foo 2>&1 | bar, dal momento che stderr di foo viene dirottato a stdout di foo, sia stdout e stderr di foo arrivare al stdin di bar.

Problemi correlati