Usar datos leídos desde una tubería en lugar de desde un archivo en las opciones de comando

18

Según la definición del hombre, este comando obtiene la entrada de un archivo.

$ command -r FILENAME

Supongamos que FILENAMEes un archivo que contiene una lista de nombres de archivos, ya que se generó usando ls > FILENAME.

¿Cómo puedo, en cambio, alimentar el comando con el resultado de lsdirectamente? En mi cabeza, algo como esto debería ser posible:

$ ls | command -r

Pero no lo hace, la salida de lsno se engancha como argumento. Salida:

Usage: command -r FILENAME
error: -r option requires an argument

¿Cómo podría obtener el efecto deseado?

Paolo
fuente
Más respuestas sobre esto en una pregunta relacionada: ¿Cómo pasar una cadena a un comando que espera un archivo?
ilkkachu

Respuestas:

31

Esto depende del comando. Algunos comandos que leen de un archivo esperan que el archivo sea un archivo normal, cuyo tamaño se conoce de antemano y que se puede leer desde cualquier posición y rebobinar. Esto es poco probable si el contenido del archivo es una lista de nombres de archivo: entonces el comando probablemente estará contenido con una tubería que simplemente leerá secuencialmente de principio a fin. Hay varias formas de alimentar datos a través de una tubería a un comando que espera un nombre de archivo.

  • Muchos comandos tratan -como un nombre especial, lo que significa leer desde la entrada estándar en lugar de abrir un archivo. Esta es una convención, no una obligación.

    ls | command -r -
  • Muchas variantes de Unix proporcionan archivos especiales /devque designan los descriptores estándar. Si /dev/stdinexiste, abrirlo y leerlo es equivalente a leerlo desde la entrada estándar; igualmente /dev/fd/0si existe.

    ls | command -r /dev/stdin
    ls | command -r /dev/fd/0
  • Si su shell es ksh, bash o zsh, puede hacer que el shell se ocupe del negocio de asignar algún descriptor de archivo. La principal ventaja de este método es que no está vinculado a la entrada estándar, por lo que puede usar la entrada estándar para otra cosa, y puede usarla más de una vez.

    command -r <(ls)
  • Si el comando espera que el nombre tenga una forma particular (generalmente una extensión particular), puede intentar engañarlo con un enlace simbólico.

    ln -s /dev/fd/0 list.foo
    ls | command -r list.foo

    O puede usar una tubería con nombre.

    mkfifo list.foo
    ls >list.foo &
    command -r list.foo

Tenga en cuenta que generar una lista de archivos lses problemático porque lstiende a alterar los nombres de los archivos cuando contienen caracteres no imprimibles. printf '%s\n' *es más confiable: imprimirá cada byte literalmente en los nombres de archivo. Los nombres de archivo que contienen líneas nuevas seguirán causando problemas, pero eso es inevitable si el comando espera una lista de nombres de archivos separados por líneas nuevas.

Gilles 'SO- deja de ser malvado'
fuente
+1 para enumerar todas las posibilidades. La sustitución del proceso de la OMI es la respuesta correcta para esta situación.
Glenn Jackman
7

Debería ser:

ls | xargs -n 1 command -r

Editar: para nombres con espacios en blanco:

ls | xargs -d '\n' -n 1 command -r
ghm1014
fuente
Esto podría ser problemático si commandcree que tiene todos los nombres de archivo que necesitará en la línea de comando de una vez. Algunas versiones de xargs darán commandalgunos nombres de archivo (10, me parece) a la vez. Parece que GNU xargs da todo de una vez.
Bruce Ediger
2

En realidad, la única solución confiable que conozco que puede manejar todos los nombres de archivos, incluidos los que tienen una nueva línea, es:

find . -maxdepth 1 -print0 | xargs -n 1 -0 command -r

El único carácter no permitido en el nombre de archivo en este caso es el carácter nulo, que de todos modos no está permitido en los nombres de archivo.

Linus Swälas
fuente
1

Muchos comandos aceptan -como un "nombre de archivo" que significa "usar la entrada estándar", pero esta convención está lejos de ser universal. Lee la página del manual.

dmckee
fuente
1

Esto debería funcionar para su propósito: ls | command -r /dev/stdin

alex
fuente