Intenté redirigir ambos stdout
y stderr
a un archivo hoy, y me encontré con esto:
<command> > file.txt 2>&1
Esto aparentemente redirige stderr
a stdout
primero, y luego el resultado stdout
se redirige a file.txt
.
Sin embargo, ¿por qué no es el pedido <command> 2>&1 > file.txt
? Uno naturalmente leería esto como (suponiendo la ejecución de izquierda a derecha) el comando que se ejecuta primero, el que stderr
se redirige stdout
y luego stdout
se escribe el resultado file.txt
. Pero lo anterior solo redirige stderr
a la pantalla.
¿Cómo interpreta el shell ambos comandos?
command-line
bash
redirect
Train Heartnet
fuente
fuente
bash
manual . Por cierto, las redirecciones no son comandos.execv
se invoca la llamada -familia syscall para entregar el subproceso al comando que se está iniciando, el shell queda fuera del bucle (ya no se ejecuta el código en ese proceso ) y no tiene forma posible de controlar lo que sucede a partir de ese momento; redirecciones por lo tanto todo debe ser realizado antes de que comience la ejecución, mientras que la cáscara tiene una copia de sí mismo se ejecuta en el proceso,fork()
ed para ejecutar su comando en.Respuestas:
Cuando ejecuta
<command> 2>&1 > file.txt
stderr se redirige2>&1
a donde va stdout actualmente, su terminal. Después de eso, stdout se redirige al archivo>
, pero stderr no se redirige con él, por lo que permanece como salida de terminal.Con
<command> > file.txt 2>&1
stdout primero se redirige al archivo>
, luego2>&1
redirige stderr a donde va stdout, que es el archivo.Puede parecer contradictorio para empezar, pero cuando piensa en las redirecciones de esta manera y recuerda que se procesan de izquierda a derecha, tiene mucho más sentido.
fuente
Puede tener sentido si lo trazas.
Al principio, stderr y stdout van a lo mismo (generalmente el terminal, al que llamo aquí
pts
):Me refiero a stdin, stdout y stderr por sus números de descriptor de archivo aquí: son descriptores de archivo 0, 1 y 2 respectivamente.
Ahora, en el primer conjunto de redirecciones, tenemos
> file.txt
y2>&1
.Asi que:
> file.txt
:fd/1
ahora va afile.txt
. Con>
,1
es el descriptor de archivo implícito cuando no se especifica nada, así que esto es1>file.txt
:2>&1
:fd/2
ahora va a donde quierafd/1
que vaya actualmente :Por otro lado, con
2>&1 > file.txt
, el orden se invierte:2>&1
:fd/2
ahora va a donde quierafd/1
que vaya, lo que significa que nada cambia:> file.txt
:fd/1
ahora va afile.txt
:El punto importante es que la redirección no significa que el descriptor de archivo redirigido seguirá todos los cambios futuros en el descriptor de archivo de destino; solo tomará el estado actual .
fuente
2.
la segunda parte;fd/1 -> file.txt
y nofd/2 -> file.txt
.Creo que ayuda pensar que el shell configurará primero la redirección a la izquierda y la completará antes de configurar la siguiente redirección.
La línea de comandos de Linux por William Shotts dice
esto tiene sentido, pero luego
pero en realidad, podemos redirigir stdout a stderr después de redirigir stderr a un archivo con el mismo efecto
Entonces, en
command > file 2>&1
, el shell envía stdout a un archivo, luego envía stderr a stdout (que se envía a un archivo). Mientras que, encommand 2>&1 > file
el shell, primero redirige stderr a stdout (es decir, lo muestra en el terminal donde normalmente va stdout) y luego, redirige stdout al archivo. TLCL es engañoso al decir que primero debemos redirigir stdout: ya que primero podemos redirigir stderr a un archivo y luego enviarle stdout. Lo que no podemos hacer es redirigir stdout a stderr o viceversa antes de redirigir a un archivo. Otro ejemploPodríamos pensar que esto eliminaría stdout al mismo lugar que stderr, pero no lo hace, redirige stdout a stderr (la pantalla) primero, y luego solo redirige stderr, como cuando lo intentamos al revés ...
Espero que esto traiga un poco de luz ...
fuente
Ya tienes algunas respuestas muy buenas. Permítanme enfatizar que hay dos conceptos diferentes involucrados aquí, cuya comprensión ayuda enormemente:
Antecedentes: descriptor de archivo versus tabla de archivo
Su descriptor de archivo es solo un número 0 ... n, que es el índice en la tabla de descriptores de archivo en su proceso. Por convención, STDIN = 0, STDOUT = 1, STDERR = 2 (tenga en cuenta que los términos,
STDIN
etc., aquí son solo símbolos / macros utilizados por convención en algunos lenguajes de programación y páginas de manual, no hay un "objeto" real llamado STDIN; para El propósito de esta discusión, STDIN es 0, etc.).Esa tabla de descriptores de archivo en sí misma no contiene ninguna información sobre cuál es el archivo real. En cambio, contiene un puntero a una tabla de archivo diferente; este último contiene información sobre un archivo físico real (o dispositivo de bloque, o tubería, o cualquier otra cosa que Linux pueda abordar a través del mecanismo de archivo) y más información (es decir, ya sea para leer o escribir).
Entonces, cuando use
>
o<
en su shell, simplemente reemplace el puntero del descriptor de archivo respectivo para apuntar a otra cosa. La sintaxis2>&1
simplemente señala el descriptor 2 a donde sea 1 punto.> file.txt
simplemente se abrefile.txt
para escribir y deja que STDOUT (descifrador de archivos 1) apunte a eso.Hay otras ventajas, por ejemplo
2>(xxx)
(es decir: crear un nuevo proceso en ejecuciónxxx
, crear una tubería, conectar el descriptor de archivo 0 del nuevo proceso al extremo de lectura de la tubería y conectar el descriptor de archivo 2 del proceso original al final de la escritura del tubo).Esta es también la base para la "magia de manejo de archivos" en otro software que no sea su shell. Por ejemplo, podría, en su secuencia de comandos Perl,
dup
colocar el descriptor de archivo STDOUT en otro (temporal) y luego volver a abrir STDOUT en un archivo temporal recién creado. A partir de este momento, toda la salida STDOUT de su propio script Perl y todas lassystem()
llamadas de ese script terminarán en ese archivo temporal. Cuando haya terminado, puede volver adup
colocar su STDOUT en el descriptor temporal en el que lo guardó, y listo, todo está como antes. Mientras tanto, incluso puede escribir en ese descriptor temporal, por lo que si bien su salida STDOUT real va al archivo temporal, aún puede enviar cosas al STDOUT real (comúnmente, el usuario).Responder
Para aplicar la información de antecedentes dada a su pregunta:
De izquierda a derecha.
fork
fuera de un nuevo proceso.file.txt
y almacene su puntero en el descriptor de archivo 1 (STDOUT).file.txt
por supuesto).exec
el<command>
Esto tendría sentido si solo hubiera una tabla, pero como se explicó anteriormente, hay dos. Los descriptores de archivos no se apuntan recíprocamente, no tiene sentido pensar "redirigir STDERR a STDOUT". El pensamiento correcto es "señalar STDERR a cualquier punto STDOUT". Si cambia STDOUT más tarde, STDERR se queda donde está, no va mágicamente junto con más cambios a STDOUT.
fuente
El orden es de izquierda a derecha. El manual de Bash ya ha cubierto lo que pides. Cita de la
REDIRECTION
sección del manual:y unas pocas líneas después:
¡Es importante tener en cuenta que la redirección se resuelve primero antes de que se ejecute ningún comando! Ver https://askubuntu.com/a/728386/295286
fuente
Siempre se deja de derecha a derecha ... excepto cuando
Al igual que en Matemáticas, lo hacemos de izquierda a derecha, excepto que la multiplicación y la división se realizan antes de la suma y la resta, excepto que las operaciones dentro del paréntesis (+ -) se realizarían antes de la multiplicación y la división.
Según la guía para principiantes de Bash aquí ( Guía para principiantes de Bash ) hay 8 órdenes de jerarquía de lo que viene primero (antes de izquierda a derecha):
Así que siempre se deja de derecha a derecha ... excepto cuando ...
fuente