¿Es seguro redirigir stdout y stderr al mismo archivo sin copias del descriptor de archivo?

27

Comienzo en el directorio vacío.

$ touch aFile
$ ls
aFile

Luego tengo lsdos argumentos, uno de los cuales no está en este directorio. Redirijo ambas secuencias de salida a un archivo llamado output. Lo uso >>para evitar escribir simultáneamente.

$ ls aFile not_exist >>output 2>>output
$ cat output
ls: cannot access 'not_exist': No such file or directory
aFile

Lo que parece funcionar. ¿Hay algún peligro para este enfoque?

salida_estado
fuente
66
Esa fue una votación rápida. Tomó como cinco segundos. ¿Me puede decir cómo es que puede evaluar el valor de mi pregunta tan rápido? Y mejor aún, ¿qué tiene de malo para poder mejorarlo?
exit_status
¿Por qué no usas el estándar más ls aFile not_exist &>>outputaquí? (Nota, supongo que está usando bash .)
FedonKadifeli
55
Porque eso no me ayuda a entender lo que estoy preguntando. Sé cómo redirigir estas transmisiones al mismo archivo, incluso de forma portátil. Lo que quiero saber es si hay algo malo con lo que sugerí en la pregunta. @FedonKadifeli
exit_status
1
@FedonKadifeli &>>NO es estándar. Es una sintaxis ambigua y DEPRECADA que funciona de manera diferente en diferentes shells. Me pregunto de dónde sacarán sus cosas.
Tío Billy
44
Bash no es un estándar . Los mandatos POSIX estándar que ls &>>foo ...deben ser analizados como dos comands ls &y >>foo ..., y esta es la forma más conchas como el /bin/shde Ubuntu se analizarlo. Para que esté en desuso, puedes mirar aquí , aunque no pretendo que sea ningún tipo de autoridad. Sin bashembargo, puede preguntar a los mantenedores si consideran que usar eso es una buena idea.
Tío Billy

Respuestas:

22

No, no es tan seguro como el estándar >>bar 2>&1.

Cuando estas escribiendo

foo >>bar 2>>bar

está abriendo el bararchivo dos veces O_APPEND, creando dos objetos de archivo completamente independientes [1], cada uno con su propio estado (puntero, modos abiertos, etc.).

Esto es muy diferente a lo 2>&1que simplemente está llamando a la llamada del dup(2)sistema, y ​​hace que los alias intercambiables stderr y stdout para el mismo objeto de archivo.

Ahora, hay un problema con eso:

O_APPENDpuede conducir a archivos corruptos en los sistemas de archivos NFS si más de un proceso agrega datos a un archivo a la vez. Esto se debe a que NFS no admite la adición de un archivo, por lo que el núcleo del cliente tiene que simularlo, lo que no se puede hacer sin una condición de carrera.

Generalmente, usted puede contar con la probabilidad de que el archivo como baren el foo >>bar 2>&1que se escriben en, al mismo tiempo desde dos lugares separados siendo bastante bajo. Pero por su >>bar 2>>barsolo lo aumentó en una docena de órdenes de magnitud, sin ninguna razón.

[1] "Descripciones de archivos abiertos" en la jerga POSIX.

Mosvy
fuente
3
Formalmente, para archivos en modo de apéndice, es seguro . El problema citado es un error en NFS que lo hace inadecuado (no compatible con POSIX) como un sistema de archivos. Sin embargo, para el caso del modo sin apéndice, nunca es seguro.
R ..
1
Eso es irrelevante. El doble apéndice del OP no es seguro de usar (además de ser completamente inútil). Y O_APPENDde todos modos es una especie de fallo, bastante oneroso de implementar correctamente.
Mosvy
Creo que la condición de carrera de NFS es solo entre diferentes clientes. El sistema operativo del cliente debe coordinar todas las escrituras entre sus procesos.
Barmar
@Barmar eso sería cierto si el sistema operativo del cliente solo se preocupara por su propia vista de un archivo nfs. Pero al escribir en un archivo nfs abierto con O_APPEND, el cliente primero recuperará el tamaño "real" del archivo del servidor ("revalidará" el inodo) y luego realizará la actualización de inodo de búsqueda + escritura + almacenamiento en caché, y solo la última parte es hecho bajo bloqueos, lo que significa que la primera parte aún podría recuperar un tamaño obsoleto del servidor y anular el correcto del inodo local / en caché. Mismo problema con lseek(SEEK_END).
Mosvy
Todavía no veo cómo eso podría causar condiciones de carrera entre dos transmisiones en el mismo cliente. Ambas corrientes deben referirse al mismo inodo local en caché.
Barmar
22

¿Qué pasa cuando lo haces?

some_command >>file 2>>file

es que filese abrirá para agregar dos veces. Esto es seguro en un sistema de archivos POSIX. Cualquier escritura que le ocurra al archivo cuando se abre para agregarla ocurrirá al final del archivo, independientemente de si los datos provienen del flujo de salida estándar o del flujo de error estándar.

Esto se basa en el soporte para operaciones de escritura de adición atómica en el sistema de archivos subyacente. Algunos sistemas de archivos, como NFS, no admiten el agregado atómico. Consulte, por ejemplo, la pregunta "¿El archivo se agrega atómico en UNIX?" En StackOverflow.

Utilizando

some_command >>file 2>&1

funcionaría incluso en NFS sin embargo.

Sin embargo, usando

some_command >file 2>file

no es seguro, ya que el shell truncará el archivo de salida (dos veces) y cualquier escritura que ocurra en cualquiera de las secuencias sobrescribirá los datos ya escritos por la otra secuencia.

Ejemplo:

$ { echo hello; echo abc >&2; } >file 2>file
$ cat file
abc
o

La hellocadena se escribe primero (con una nueva línea de terminación), y luego la cadena abcseguida de una nueva línea se escribe a partir del error estándar, sobrescribiendo el hell. El resultado es la cadena abccon una nueva línea, seguida de lo que queda de la primera echosalida, una oy una nueva línea.

Intercambiar los dos echoalrededor de la herida produce solo helloen el archivo de salida, ya que esa cadena se escribe en último lugar y es más larga que la abccadena. El orden en que ocurren las redirecciones no importa.

Sería mejor y más seguro usar los más idiomáticos.

some_command >file 2>&1
Kusalananda
fuente
1
Si bien eso es cierto para los shells modernos, ese no fue el caso en el shell Bourne o Thomson (de dónde >>proviene), donde >>se abriría para escribir y buscar hasta el final (supongo que O_APPEND aún no se inventó en ese momento). Incluso en Solaris 10, /bin/sh -c '(echo a; echo b >&2) >> file 2>> file; cat file'salidas b.
Stéphane Chazelas
@ StéphaneChazelas ¿Es un problema con la implementación de Solaris 10 sho con su sistema de archivos?
Kusalananda
1
Eso es lo que >>estaba haciendo originalmente, no se estaba abriendo con O_APPEND, se estaba abriendo sin buscar el final. No es tanto un problema, es lo que estaba haciendo y fue documentado para hacerlo.
Stéphane Chazelas
0

Depende de lo que quieras lograr. Depende de usted decidir si está bien tener errores en el mismo archivo que la salida. Esto es solo guardar texto en un archivo con la funcionalidad del shell que le permite redirigir como lo desee. No hay un sí o un no absoluto. Como todo en Linux se puede hacer de varias maneras, esta es mi forma de ls notExistingFile existingFile >> output 2>&1 responder la pregunta: en términos de la redirección en sí, sí, es perfectamente seguro.

Ángel
fuente
Hay más de lo que estás diciendo aquí. El mismo ejercicio con en >lugar de >>sobrescribirá algunos caracteres. Entonces, no es solo que el shell me permite redirigir, porque cuando redirecciono con >, el resultado es diferente. Entonces hay matices con >, ¿hay alguno con >>?
exit_status
Sí, será diferente. Como dije, depende de tu objetivo >: sobrescribir. >>- agregar
Ángel