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 sintaxis 2>&1
simplemente señala el descriptor 2 a donde sea 1 punto. > file.txt
simplemente se abre file.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ón xxx
, 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 las system()
llamadas de ese script terminarán en ese archivo temporal. Cuando haya terminado, puede volver a dup
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:
¿En qué orden el shell ejecuta comandos y redirige la transmisión?
De izquierda a derecha.
<command> > file.txt 2>&1
fork
fuera de un nuevo proceso.
- Abra
file.txt
y almacene su puntero en el descriptor de archivo 1 (STDOUT).
- Apunte STDERR (descriptor de archivo 2) a lo que sea que apunte fd 1 en este momento (que de nuevo es el ya abierto,
file.txt
por supuesto).
exec
el <command>
Aparentemente, esto redirige stderr a stdout primero, y luego el stdout resultante se redirige a file.txt.
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.