He estado estudiando sobre la línea de comando y aprendí que |(pipeline) está destinado a redirigir la salida de un comando a la entrada de otro. Entonces, ¿por qué el comando ls | fileno funciona?
file input es uno de los más nombres de archivo, como file filename1 filename2
lsLa salida es una lista de directorios y archivos en una carpeta, así que pensé que ls | filese suponía que debía mostrar el tipo de archivo de cada archivo en una carpeta.
Sin embargo, cuando lo uso, el resultado es:
Usage: file [-bcEhikLlNnprsvz0] [--apple] [--mime-encoding] [--mime-type]
[-e testname] [-F separator] [-f namefile] [-m magicfiles] file ...
file -C [-m magicfiles]
file [--help]
Como hubo algún error con el uso del filecomando
command-line
ls
pipe
file-command
IanC
fuente
fuente

ls, indica que desea que todos los archivos en el directorio actual se manejen con elfilecomando. ... Entonces, ¿por qué no hacer simplemente:,file *que responderá con una línea para cada archivo, carpeta.file *es la forma más inteligente, me preguntaba por quélsno funcionaba la salida. Duda despejada :)lses generalmente una mala idea.Respuestas:
El problema fundamental es que
fileespera nombres de archivo como argumentos de línea de comandos, no en stdin. Cuando escribe,ls | filela salida delsse pasa como entrada afile. No como argumentos, como entrada.¿Cual es la diferencia?
Los argumentos de la línea de comandos son cuando escribe banderas y nombres de archivos después de un comando, como en
cmd arg1 arg2 arg3. En los scripts de shell estos argumentos están disponibles como las variables$1,$2,$3, etc. En C es posible acceder a ellos a través dechar **argvyint argcargumentos amain().La entrada estándar, stdin, es un flujo de datos. Algunos programas tienen gusto
catowcleen de stdin cuando no se les da ningún argumento de línea de comandos. En un script de shell puede usarreadpara obtener una sola línea de entrada. En C puedes usarscanf()ogetchar(), entre varias opciones.filenormalmente no lee de stdin. Espera que se pase al menos un nombre de archivo como argumento. Es por eso que imprime el uso cuando escribels | file, porque no pasó una discusión.Puede usar
xargspara convertir stdin en argumentos, como enls | xargs file. Aún así, como menciona Terdon , analizarlses una mala idea. La forma más directa de hacer esto es simplemente:fuente
filea obtener nombres de archivos de su entrada, usandols | file -f -. Sigue siendo una mala idea de c.lsla salida enfileel stdin. Pruébalo.file $(ls), que también funciona, de otra manera.Porque, como dices, la entrada de
filetiene que ser nombres de archivo . La salida dels, sin embargo, es solo texto. El hecho de que sea una lista de nombres de archivos no cambia el hecho de que es simplemente texto y no la ubicación de los archivos en el disco duro.Cuando ve un resultado impreso en la pantalla, lo que ve es texto. Si ese texto es un poema o una lista de nombres de archivos no hace ninguna diferencia para la computadora. Todo lo que sabe es que es texto. Esta es la razón por la que puede pasar la salida de los
lsprogramas que toman el texto como entrada (aunque realmente no debería ):Entonces, para usar la salida de un comando que enumera los nombres de los archivos como texto (como
lsofind) como entrada para un comando que toma nombres de archivos, debe usar algunos trucos. La herramienta típica para esto esxargs:Sin embargo, como dije antes, realmente no quieres analizar la salida de
ls. Algo asífindes mejor (print0imprime un en\0lugar de una nueva línea después de cada nombre de archivo y el-0dexargspermite que se ocupe de dicha entrada; este es un truco para que sus comandos funcionen con nombres de archivo que contienen nuevas líneas):Que también tiene su propia forma de hacerlo, sin necesidad
xargsde nada:Finalmente, también puedes usar un bucle de shell. Sin embargo, tenga en cuenta que, en la mayoría de los casos,
xargsserá mucho más rápido y más eficiente. Por ejemplo:fuente
fileno parece que realmente leer la entrada estándar a menos que reciba una explícita-marcador de posición: compararfile foo,echo foo | fileyecho foo | file -; de hecho, que es probablemente la razón para el mensaje de uso en el caso de los programas operativos (es decir, que no es realmente debido a la salida delses "simplemente el texto", sino más bien porque la lista de argumentos quefileestá vacía)echo foo | file -realidad no se ejecutafileen el archivofoosino en la secuencia stdin.cateso, excepto stdin sin,-excepto cuando se dan argumentos de archivo, creo?No "redirige" la salida, sino que toma la salida de un programa y la usa como entrada, mientras que el archivo no toma entradas sino los nombres de archivo como argumentos , que luego se prueban. Las redirecciones no pasan estos nombres de archivo como argumentos ni las tuberías , más tarde lo que está haciendo.
Lo que puede hacer es leer los nombres de archivo de un archivo con la
--files-fromopción si tiene un archivo que enumera todos los archivos que desea probar, de lo contrario, simplemente pase las rutas a sus archivos como argumentos.fuente
La respuesta aceptada explica por qué el comando de tubería no funciona de inmediato, y con el
file *comando, ofrece una solución simple y directa.Me gustaría sugerir otra alternativa que podría ser útil en algún momento. El truco es usar el
(`)carácter de retroceso . El backtick se explica con gran detalle aquí . En resumen, toma el resultado del comando encerrado en los backticks y lo sustituye como una cadena en el comando restante.Por lo tanto,
find `ls`tomará la salida dellscomando y la sustituirá como argumentos para elfindcomando. Esto es más largo y más complicado que la solución aceptada, pero sus variantes pueden ser útiles en otras situaciones.fuente
command(no puedo encontrar el código de barra diagonal inversa en mi teléfono) para expandir la salida de un comando en el bash y usarlo como parámetro para otros comandos. Realmente útil, aunque usarlo en este caso (con ls ) aún resultaría en algunos problemas debido a los caracteres especiales en algunos nombres de archivo.La salida de a
lstravés de una tubería es un bloque sólido de datos con 0x0a que separa cada línea, es decir, un carácter de salto de línea, yfileobtiene esto como un parámetro, donde espera que varios caracteres funcionen en uno a la vez.Como regla general, nunca lo use
lspara generar una fuente de datos para otros comandos: un día se canalizará ... ¡rmy luego tendrá problemas!Es mejor usar un bucle, como el
for i in *; do file "$i" ; doneque producirá la salida que desea, de manera predecible. Las comillas están ahí en caso de nombres de archivo con espacios.fuente
file *;-)lses una muy, muy mala idea . No solo porque puede pasarlo a algo dañino comorm, más importante porque se rompe en cualquier nombre de archivo no estándar.rmToma nombres de archivos de entrada estándar? Yo creo que no. Además, como regla general,lsha sido uno de los principales ejemplos de una fuente de datos para el uso de tuberías de Unix desde el comienzo de Unix. Es por eso que el valor predeterminado es un simple nombre de archivo por línea sin atributos ni adornos cuando su salida es una tubería, a diferencia de su formato predeterminado habitual cuando la salida es el terminal.Si desea usar una tubería para alimentar,
fileuse la opción-fque normalmente va seguida de un nombre de archivo, pero también puede usar un solo guión-para leer desde la entrada estándar, entoncesEl truco con el guión
-funciona con muchas de las utilidades de línea de comandos estándar (aunque a--veces lo es ), por lo que siempre vale la pena intentarlo.La herramienta
xarges mucho más poderosa y en la mayoría de los casos solo es necesaria si la lista de argumentos es demasiado larga (vea esta publicación para más detalles).fuente
--? Nunca he visto eso.--es típicamente el indicador de "fin de banderas".Funciona usando el comando como a continuación
Me va a funcionar mejor
fuente
Esto también debería funcionar:
como también se discute aquí: https://unix.stackexchange.com/questions/5778/whats-the-difference-between-stuff-and-stuff
fuente
$()método es el mismo que el método de retroceso en askubuntu.com/a/795744/158442