¿Por qué el comportamiento de `command 1> file.txt 2> file.txt` es diferente de` command 1> file.txt 2> & 1`?

20

Cuando desee redirigir stdout y stderr al mismo archivo, puede hacerlo utilizando command 1>file.txt 2>&1, o command &>file.txt. Pero, ¿por qué el comportamiento es command 1>file.txt 2>file.txtdiferente de los dos comandos anteriores?

El siguiente es un comando de verificación.

$ cat redirect.sh
#!/bin/bash

{ echo -e "output\noutput" && echo -e "error" 1>&2; } 1>file.txt 2>&1
{ echo -e "output\noutput" && echo -e "error" 1>&2; } 1>file1.txt 2>file1.txt
{ echo -e "error" 1>&2 && echo -e "output\noutput"; } 1>file2.txt 2>file2.txt
{ echo -e "output" && echo -e "error\nerror" 1>&2; } 1>file3.txt 2>file3.txt
{ echo -e "error\nerror" 1>&2 && echo -e "output"; } 1>file4.txt 2>file4.txt

$ ./redirect.sh

$ echo "---file.txt---"; cat file.txt;\
echo "---file1.txt---"; cat file1.txt; \
echo "---file2.txt---"; cat file2.txt; \
echo "---file3.txt---"; cat file3.txt; \
echo "---file4.txt----"; cat file4.txt;
 ---file.txt---
output
output
error
---file1.txt---
error

output
---file2.txt---
output
output
---file3.txt---
error
error
---file4.txt----
output
rror

En lo que respecta a los resultados, parece que la segunda cadena de eco sobrescribe la primera cadena de eco cuando ejecuta command 1>file.txt 2>file.txt, pero no sé por qué lo hará. (¿Hay alguna referencia en alguna parte?)

fhiyo
fuente

Respuestas:

43

Necesitas saber dos cosas:

  • Un descriptor de archivo abierto conocido por el lado del modo de aplicación de un proceso hace referencia a un objeto interno del núcleo conocido como descripción de archivo , que es una instancia de un archivo abierto. Puede haber múltiples descripciones de archivo por archivo y múltiples descriptores de archivo que comparten una descripción de archivo.
  • La posición actual del archivo es un atributo de una descripción de archivo . Por lo tanto, si varios descriptores de archivo se asignan a una única descripción de archivo, todos comparten la misma posición de archivo actual, y un cambio en la posición de archivo promulgada usando uno de esos descriptores de archivo afecta a todos los demás descriptores de archivo.

    Tales cambios son promulgadas por procesos llamando a la read()/ readv(), write()/ writev(), lseek()llamadas, y cosas por el estilo del sistema. El echocomando llama write()/ writev()por supuesto.

Entonces, lo que sucede es esto:

  • command 1>file.txt 2>&1solo crea una descripción de archivo, porque el shell solo abre un archivo una vez. Las marcas de concha tanto la salida estándar y descriptores de archivo error estándar se asignan a esa descripción solo archivo. Se duplica la salida estándar a error estándar. Por lo tanto, una escritura a través de cualquier descriptor de archivo moverá la posición actual del archivo compartido: cada escritura va después de la escritura anterior de la descripción del archivo común. Y como puede ver, los resultados de los echocomandos no se sobrescriben entre sí.
  • command 1>file.txt 2>file.txtcrea dos descripciones de archivo, porque el shell abre el mismo archivo dos veces, en respuesta a las dos redirecciones explícitas. La salida estándar y los descriptores de archivo de error estándar se asignan a dos descripciones de archivo diferentes, que a su vez se asignan al mismo archivo único. Las dos descripciones de archivo tienen posiciones de archivo actuales completamente independientes, y cada escritura va inmediatamente a la escritura anterior en la misma descripción de archivo. Y como puede ver, el resultado es que lo que se escribe a través de uno puede sobrescribir lo que se escribe a través del otro, de varias maneras diferentes según el orden en el que ejecuta las escrituras.

Otras lecturas

JdeBP
fuente
1
Debe ser la descripción del archivo abierto en lugar de la descripción del archivo . Se trata más del registro de cómo se abrió el archivo más que del archivo en sí. Esa es la terminología utilizada por POSIX, Linux, Solaris y la documentación de GNU al menos.
Stéphane Chazelas
16

Usar >le dice que sobrescriba el archivo. Como tiene stdout y stderr escribiendo en el archivo en dos operaciones diferentes, la última en escribir sobrescribirá la primera.

Tu puedes hacer:

command 1>>file.txt 2>>file.txt

o

command &>file.txt Solo bash v4 y superior.

>> le dice que agregue el archivo para que no reemplace el resultado de las operaciones anteriores.

&> es solo una forma más fácil de escribir 2>&1

Jesse_b
fuente
2
¿Por qué ls 1>&0y ls 0>&0todavía muestra la salida de ls?
Yvain
Me sorprende que usar >>obras. ¿Por qué esto no tiene el problema de dos descripciones de archivos con compensaciones independientes? @JdeBP, ¿sabes? Pensé que abrir un archivo en modo agregar era equivalente a abrir en modo de escritura, buscar la posición final y luego no permitir más búsquedas.
JoL
44
@jlmg: los archivos en modo Append son buscables, pero cada escritura tiene como prefijo una búsqueda implícita hasta el final. Si esa búsqueda implícita es atómica es menos claro para mí.
Kevin
1
Eso depende completamente de la pregunta de seguimiento. Unos como este, que están relacionados, indican que la respuesta está incompleta. Y es poco probable que la gente busque las preguntas de seguimiento sin querer saber la respuesta completa. Por lo tanto, es muy probable que el seguimiento de las preguntas tenga respuestas que duplican la información y, por lo tanto, se cierren como duplicados.
Trly
1
@Kevin, en sistemas de archivos totalmente compatibles con POSIX, la búsqueda implícita de O_APPEND es atómica. Dicho esto, no todos los sistemas de archivos implementan la semántica relevante correctamente, por ejemplo, NFS (al menos v3 y anteriores, no estoy claro en v4) no tiene el soporte relevante integrado en el protocolo de conexión, por lo que el servidor tiene no hay forma de saber si un cliente abrió un archivo con O_APPEND.
Charles Duffy