¿Por qué hace $ ls > ls.out
que 'ls.out' se incluya en la lista de nombres de archivos en el directorio actual? ¿Por qué se eligió esto? ¿Por qué no de otra manera?
command-line
bash
redirect
Edward Torvalds
fuente
fuente
ls > ../ls.out
Respuestas:
Al evaluar el comando, la
>
redirección se resuelve primero: por lo tanto, para cuando sels
ejecute el archivo de salida ya se ha creado.Esta es también la razón por la cual leer y escribir en el mismo archivo usando una
>
redirección dentro del mismo comando trunca el archivo; para cuando el comando ejecuta el archivo ya se ha truncado:Trucos para evitar esto:
<<<"$(ls)" > ls.out
(funciona para cualquier comando que deba ejecutarse antes de que se resuelva la redirección)La sustitución del comando se ejecuta antes de evaluar el comando externo, por lo que
ls
se ejecuta antes dels.out
crearse:ls | sponge ls.out
(funciona para cualquier comando que deba ejecutarse antes de que se resuelva la redirección)sponge
escribe en el archivo solo cuando el resto de la tubería ha terminado de ejecutarse, por lo quels
se ejecuta antes dels.out
crearse (sponge
se proporciona con elmoreutils
paquete):ls * > ls.out
(funciona parals > ls.out
el caso específico)La expansión del nombre de archivo se realiza antes de que se resuelva la redirección, por lo que
ls
se ejecutará en sus argumentos, que no contendránls.out
:Sobre por qué las redirecciones se resuelven antes de que se ejecute el programa / script / lo que sea, no veo una razón específica por la que sea obligatorio hacerlo, pero veo dos razones por las que es mejor hacerlo:
no redirigir STDIN de antemano haría que el programa / script / lo que sea se mantenga hasta que se redirija STDIN;
no redirigir STDOUT de antemano necesariamente debe hacer que el shell buffer sea la salida del programa / script / lo que sea hasta que STDOUT sea redirigido;
Entonces, una pérdida de tiempo en el primer caso y una pérdida de tiempo y memoria en el segundo caso.
Esto es justo lo que se me ocurre, no estoy afirmando que estas sean las razones reales; pero supongo que, en general, si uno tuviera una opción, iría con la redirección antes de todos modos por las razones mencionadas anteriormente.
fuente
De
man bash
:La primera oración sugiere que la salida debe ir a otro lugar que no sea la
stdin
redirección justo antes de ejecutar el comando. Por lo tanto, para ser redirigido a un archivo, primero el archivo debe ser creado por el propio shell.Para evitar tener un archivo, le sugiero que redirija la salida a la tubería con nombre primero y luego al archivo. Tenga en cuenta el uso de
&
para devolver el control sobre el terminal al usuario¿Pero por qué?
Piensa en esto: ¿dónde estará la salida? Un programa cuenta con funciones como
printf
,sprintf
,puts
, todos los cuales por defecto ir astdout
, pero puede su producción se ha ido al archivo si el archivo no existe en el primer lugar? Es como el agua ¿Puedes tomar un vaso de agua sin poner primero un vaso debajo del grifo?fuente
No estoy en desacuerdo con las respuestas actuales. El archivo de salida debe abrirse antes de que se ejecute el comando o el comando no tendrá ningún lugar para escribir su salida.
Esto se debe a que "todo es un archivo" en nuestro mundo. La salida a la pantalla es SDOUT (también conocido como descriptor de archivo 1). Para que una aplicación escriba en el terminal, abre fd1 y le escribe como un archivo.
Cuando redirige la salida de una aplicación en un shell, está alterando fd1, por lo que en realidad apunta al archivo. Cuando canaliza, modifica el STDOUT de una aplicación para convertirse en el STDIN de otra (fd0).
Pero es bueno decir eso, pero puedes ver fácilmente cómo funciona esto
strace
. Es bastante pesado, pero este ejemplo es bastante corto.Dentro
strace.out
podemos ver los siguientes puntos destacados:Esto se abre
ls.out
comofd3
. Escribir solamente. Trunca (sobrescribe) si existe, de lo contrario crea.Esto es un poco de malabarismo. Desviamos STDOUT (fd1) a fd10 y lo cerramos. Esto se debe a que no estamos enviando nada al STDOUT real con este comando. Termina duplicando el controlador de escritura
ls.out
y cerrando el original.Esto es buscando el ejecutable. Una lección tal vez para no tener un largo camino;)
Luego se ejecuta el comando y el padre espera. Durante esta operación, cualquier STDOUT se habrá asignado al identificador de archivo abierto
ls.out
. Cuando el hijo emiteSIGCHLD
, esto le dice al proceso principal que ha finalizado y que puede reanudar. Termina con un poco más de malabarismo y un cierre dels.out
.¿Por qué hay por lo tanto malabarismo? No, tampoco estoy completamente seguro.
Por supuesto que puedes cambiar este comportamiento. Puede almacenar en la memoria intermedia algo así
sponge
y eso será invisible desde el comando en curso. Todavía estamos afectando a los descriptores de archivos, pero no de manera visible en el sistema de archivos.fuente
También hay un buen artículo sobre Implementación de operadores de redirección y canalización en shell . Lo que muestra cómo se podría implementar la redirección para
$ ls > ls.out
que se vea así:fuente