2012-07-19 15 views
10

Cuando se combina stderr con stdout, ¿por qué 2>&1 debe aparecer antes de | (tubería) pero después de > myfile (redirigir al archivo)?¿Por qué 2> y 1 deben aparecer antes de | (tubería) pero después de un "> miarchivo" (redirigir al archivo)?

Para redirigir stderr a stdout para la salida de archivo:

echo > myfile 2>&1 

Para redirigir stderr a stdout para una tubería:

echo 2>&1 | less 



Mi suposición era que simplemente lo que podía hacer:

echo | less 2>&1 

y woul d trabajo, pero no es así. Por qué no?

Respuesta

16

Una tubería es una | lista de -delimited comandos. Las redirecciones que especifique se aplican a los comandos constituyentes (simples o compuestos), pero no a la interconexión como un todo. Cada tubería encadena la salida estándar de un comando al estándar de la siguiente aplicando implícitamente un redireccionamiento a cada subcadena antes de se evalúan las redirecciones asociadas con un comando.

cmd 2>&1 | less 

Primera salida estándar de la primera subcapa se redirige a la tubería de la que es la lectura less. A continuación, se aplica el redireccionamiento 2>&1 al primer comando. Redirigir stderr a stdout funciona porque stdout ya está apuntando a la tubería.

cmd | less 2>&1 

Aquí, la redirección se aplica a less. El std y el stderr de Less comenzaron presumiblemente hacia la terminal, por lo que 2>&1 en este caso no tiene ningún efecto.

Si desea una redirección a aplicar a una tubería entera, para agrupar varios comandos como parte de una tubería, o para tuberías nido, a continuación, utilizar un grupo de comandos (o cualquier otra comando compuesto):

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

Puede ser un ejemplo típico. El resultado final es: cmd1 y cmd2's stderr ->cmd3; cmd2 's stdout ->cmd3; y cmd1 y cmd3's stderr, y cmd3's stdout -> el terminal.

Si usa el tubo Bash-specific |&, las cosas se vuelven extrañas, porque cada una de las redirecciones estándar de la tubería todavía se produce primero, pero la redirección de stderr es la última. Por ejemplo:

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

Ahora, en contra de la intuición, todos los resultados están ocultos. La primera extensión de f va a la tubería, la siguiente extensión de f se redirige a /dev/null, y finalmente, stderr se redirige a stdout (/dev/null todavía).

Recomiendo nunca usar |& en Bash - se usa aquí para la demostración.

+3

+1 bien explicado – jordanm

+2

+1. Lo único que agregaría es que una tubería es un separador de comandos, como punto y coma. –

+1

+1 ¡Guau, excelente respuesta @ormaaj! Exactamente lo que estaba buscando, ¡gracias! –

6

Para añadir a la respuesta de ormaaj:

La razón es necesario especificar operadores de redirección en el orden correcto es que son evaluados de izquierda a derecha. Tenga en cuenta estas listas de comandos:

# 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

Mi respuesta es mediante la comprensión de los descriptores de fichero. Cada proceso tiene un grupo de descriptores de archivos: entradas a los archivos que se abren. Por defecto, el número 0 es para stdin, el número 1 para stdout y el número 2 para stderr.

Los redirectores de E/S> y < se conectan por defecto a sus descriptores de archivos más razonables, stout y stdin. Si redestina stdout a un archivo (como con foo > bar), en el proceso de inicio 'foo', la 'barra' del archivo se abre para escribir y se engancha en el descriptor de archivo número 1. Si solo desea stderr en 'barra', usted Usaría foo 2> bar, que abre la barra de archivos y la engancha al stderr.

Ahora el redirector de E/S '2> & 1'. Normalmente leo eso como 'poner el descriptor 2 de archivo al mismo nivel que el descriptor de archivo 1. Mientras lee la línea de comando de izquierda a derecha, puede hacer lo siguiente: foo 1>bar 2>&1 1>/dev/tty. Con esto, el descriptor de archivo 1 se establece en el archivo 'bar', el descriptor de archivo 2 se establece en el mismo que 1 (de ahí 'bar') y después de eso, el descriptor de archivo 1 se establece en/dev/tty. El runnning foo está enviando su salida a/dev/tty y stderr al archivo 'bar'.

Ahora viene la tubería: esto no altera los descriptores de archivos, sin embargo, los conectará entre los procesos: stdout del proceso izquierdo de stdin del siguiente. Stderr se transmite. Por lo tanto, si le gusta que la tubería trabaje en stderr solamente, use foo 2| bar, que conecta stderr de foo a stdin de bar. (No estoy seguro de lo que sucede con la salida estándar de foo.)

Con lo anterior, si se utiliza foo 2>&1 | bar, ya stderr de foo es enviada a la salida estándar de foo, stdout y stderr de foo llegar a la stdin de bar.

Cuestiones relacionadas