¿Es> & - más eficiente que> / dev / null?


58

Ayer leí este comentario SO que dice que en el shell (al menos bash) >&-"tiene el mismo resultado que" >/dev/null.

Ese comentario en realidad se refiere a la guía de ABS como la fuente de su información. Pero esa fuente dice que la >&-sintaxis "cierra los descriptores de archivo".

No me queda claro si las dos acciones de cerrar un descriptor de archivo y redirigirlo al dispositivo nulo son totalmente equivalentes. Entonces mi pregunta es: ¿lo son?

A primera vista, parece que cerrar un descriptor es como cerrar una puerta, pero redirigirlo a un dispositivo nulo es abrir una puerta al limbo. Los dos no me parecen exactamente iguales porque si veo una puerta cerrada, no intentaré arrojar nada, pero si veo una puerta abierta, supondré que puedo.

En otras palabras, siempre me he preguntado si >/dev/nullsignifica que cat mybigfile >/dev/nullrealmente procesaría cada byte del archivo y lo escribiría para /dev/nullolvidarlo. Por otro lado, si la cáscara se encuentra con un descriptor de archivo cerrado tiendo a pensar (pero no estoy seguro) que simplemente no escribirá nada, aunque la cuestión sigue siendo si cattodavía se lee cada byte.

Este comentario dice >&-y >/dev/null" debería " ser el mismo, pero no es una respuesta tan contundente para mí. Me gustaría tener una respuesta más autorizada con alguna referencia al núcleo estándar o fuente o no ...


Si quisiera ser caritativo, diría que el comentario no significa que se implementaron de la misma manera, solo que ambos tienen el mismo resultado final de evitar que vea la salida del programa.
Barmar

Respuestas:


71

No, ciertamente no desea cerrar los descriptores de archivo 0, 1 y 2.

Si lo hace, la primera vez que la aplicación abra un archivo, se convertirá en stdin / stdout / stderr ...

Por ejemplo, si haces:

echo text | tee file >&-

Cuando tee(al menos algunas implementaciones, como busybox ') abre el archivo para escribir, se abrirá en el descriptor de archivo 1 (stdout). Entonces teeescribiremos textdos veces en file:

$ echo text | strace tee file >&-
[...]
open("file", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 1
read(0, "text\n", 8193)                 = 5
write(1, "text\n", 5)                   = 5
write(1, "text\n", 5)                   = 5
read(0, "", 8193)                       = 0
exit_group(0)                           = ?

Se sabe que causa vulnerabilidades de seguridad. Por ejemplo:

chsh 2>&-

Y chsh(una aplicación setuid) puede terminar escribiendo mensajes de error /etc/passwd.

Algunas herramientas e incluso algunas bibliotecas intentan protegerse contra eso. Por ejemplo, GNU teemoverá el descriptor de archivo a uno arriba de 2 si los archivos que abre para escribir se asignan 0, 1, 2 mientras que busybox teeno lo hará.

La mayoría de las herramientas, si no pueden escribir en stdout (porque, por ejemplo, no está abierto), informarán un mensaje de error en stderr (en el idioma del usuario, lo que significa un procesamiento adicional para abrir y analizar archivos de localización ...). será significativamente menos eficiente y posiblemente haga que el programa falle.

En cualquier caso, no será más eficiente. El programa seguirá haciendo una write()llamada al sistema. Solo puede ser más eficiente si el programa deja de escribir en stdout / stderr después de la primera write()llamada fallida del sistema, pero los programas generalmente no lo hacen. Generalmente salen con un error o siguen intentándolo.


44
Creo que esta respuesta sería aún mejor si el párrafo final estuviera en la parte superior (ya que es lo que responde más directamente a la pregunta del OP), y luego discutió por qué es una mala idea, incluso si funcionó principalmente. Pero lo tomaré, tengo un voto a favor. ;)
un CVn

@ StéphaneChazelas: Como dijo Michael, hubiera esperado el último párrafo en la parte superior, pero gracias por aclararlo, en realidad probablemente solo crea problemas. Entonces, supongo que una pregunta auxiliar sería, ¿cuándo será útil cerrar un FD? ¿O debería hacer eso como una pregunta separada?
jamadagni


1
@jamadagni Si el enlace proporcionado por Stéphane no responde a la pregunta, diría que suena como el comienzo de una pregunta separada, ya que no está directamente relacionada con la eficacia relativa de los dos métodos.
un CVn

1
Aprecio que Stephane comience con esta advertencia de seguridad importante, ya que sería menos visible si el último párrafo estuviera en la parte superior. +1 de mi parte
Olivier Dulac

14

IOW Siempre me he preguntado si >/dev/nullsignifica que cat mybigfile >/dev/nullrealmente procesaría cada byte del archivo y lo escribiría, lo que /dev/nulllo olvida.

No es una respuesta completa a su pregunta, pero sí, lo anterior es cómo funciona.

catlee los archivos con nombre, o la entrada estándar si no se nombra ningún archivo, y envía a su salida estándar el contenido de esos hasta que encuentra un EOF en (último ingreso estándar) el último archivo nombrado. Ese es su trabajo.

Al agregar >/dev/null, está redirigiendo la salida estándar a / dev / null. Ese es un archivo especial (un nodo de dispositivo) que arroja todo lo escrito en él (e inmediatamente devuelve EOF en la lectura). Tenga en cuenta que la redirección de E / S es una característica proporcionada por el shell, no por cada aplicación individual, y que no hay nada mágico en el nombre / dev / null, solo lo que existe allí en la mayoría de los sistemas tipo Unix .

También es importante tener en cuenta que la mecánica específica de los nodos de dispositivo varía de un sistema operativo a otro, pero cat (que, en un sistema GNU, significa coreutils) es multiplataforma (el mismo código fuente debe ejecutarse al menos en Linux y Hurd) y, por lo tanto, no puede tomar dependencias de núcleos específicos del sistema operativo. Además, aún funciona si crea un alias / dev / null (en Linux, esto significa un nodo de dispositivo con el mismo número de dispositivo mayor / menor) con otro nombre. Y siempre existe el caso de escribir en otro lugar que se comporte efectivamente igual (digamos, / dev / zero).

De ello se deduce que catdesconoce las propiedades especiales de / dev / null, y de hecho es probable que desconozca la redirección en primer lugar, pero aún necesita realizar exactamente el mismo trabajo: lee los archivos nombrados y genera el contenido de el / esos archivo (s) a su salida estándar. Que la salida estándar de catpase a un vacío no es algo en catsí mismo.


2
Para extender su respuesta: Sí, cat mybigfile > /dev/nullhará catque lea cada byte de bigfilememoria. Y, por cada nbytes, llamará write(1, buffer, n). Sin el conocimiento del catprograma, writeno hará absolutamente nada (excepto tal vez por una contabilidad trivial). Escribir en /dev/nullno requiere procesar cada byte.
G-Man dice 'reinstalar a Monica' el

2
Recuerdo que me quedé impresionado cuando leí la fuente del kernel de Linux del dispositivo / dev / null. Esperaba que hubiera algún sistema elaborado de buffers de liberación (), etc., pero, no, es básicamente un retorno ().
Brian Minton

44
@ G-Man: No sé si puedes garantizar que eso sea cierto en todos los casos. No puedo encontrar evidencia ahora, pero recordar algunos aplicación de cualquiera cato cpde que trabajaría por mmaping grandes trozos de archivo de origen en la memoria, a continuación, llamar write()en la región asignada. Si estuviera escribiendo /dev/null, la write()llamada volvería de inmediato sin fallar en las páginas del archivo fuente, por lo que en realidad nunca se leería desde el disco.
Nate Eldredge

2
Además, algo como GNU catse ejecuta en muchas plataformas, pero una mirada casual al código fuente mostrará muchos #ifdefs: no es literalmente el mismo código que se ejecuta en todas las plataformas, y hay muchas secciones dependientes del sistema.
Nate Eldredge

@NateEldredge: Punto interesante, pero solo estaba aprovechando la respuesta de Michael, por lo que no me estás contradiciendo tanto como a Michael.
G-Man dice 'reinstalar a Monica' el
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.