Cómo canalizar la lista de archivos devueltos por el comando find a cat para ver todos los archivos

204

Estoy haciendo una findy luego obtengo una lista de archivos. ¿Cómo lo canalizo a otra utilidad como cat(para que cat muestre el contenido de todos esos archivos) y básicamente necesito grepalgo de estos archivos?

Devang Kamdar
fuente

Respuestas:

340
  1. Tubería a otro proceso (aunque esto NO logrará lo que dijo que está tratando de hacer):

    command1 | command2
    

    Esto enviará la salida del comando1 como la entrada del comando2

  2. -execen un find(esto hará lo que desea hacer, pero es específico para find)

    find . -name '*.foo' -exec cat {} \;
    

    (Todo entre findy -execson los predicados de búsqueda que ya estaba usando. {}Sustituirá el archivo particular que encontró en el comando ( cat {}en este caso); \;es para finalizar el -execcomando).

  3. enviar la salida de un proceso como argumentos de línea de comando a otro proceso

    command2 `command1`
    

    por ejemplo:

    cat `find . -name '*.foo' -print`
    

    (Tenga en cuenta que estos no son VOLVER comillas citas regulares (bajo la tilde ~ en mi teclado).) Esto enviará la salida command1en command2como argumentos de línea de comandos. Sin embargo, tenga en cuenta que los nombres de archivo que contienen espacios (líneas nuevas, etc.) se dividirán en argumentos separados.

kenj0418
fuente
2
gato find -name '*.foo' -printfuncionó muy bien para mí ... Gracias
Devang Kamdar
Las comillas traseras funcionan muy bien y están más generalizadas, también puede usar esto para obtener una lista de archivos de un archivo.
Hazok
11
Tenga en cuenta que las versiones modernas de le findpermiten escribir:, find . -name '*.foo' -exec cat {} +donde +indica que finddebe agrupar tantos nombres de archivos como sea conveniente en una sola invocación de comando. Esto es bastante útil (se trata de espacios, etc. en los nombres de archivo sin recurrir a -print0y xargs -0).
Jonathan Leffler
18
Sin mencionar:find . -name '*.foo' | xargs cat
stewSquared
3
Solo para agregar la respuesta de @stewSquared: Para encontrar todas las líneas en los archivos que contienen una cadena determinada, hagafind . -name '*.foo' | xargs cat | grep string
Bim
84

Versión moderna

POSIX 2008 agregó el +marcador, lo findque significa que ahora agrupa automáticamente tantos archivos como sea razonable en una sola ejecución de comando, de manera muy similar xargs, pero con una serie de ventajas:

  1. No tiene que preocuparse por los caracteres extraños en los nombres de archivo.
  2. No tiene que preocuparse de que el comando se invoque con cero nombres de archivo.

El problema del nombre del archivo es un problema xargssin la -0opción, y el problema de 'ejecutar incluso con cero nombres de archivo' es un problema con o sin la -0opción, pero GNU xargstiene la opción -ro --no-run-if-emptypara evitar que eso suceda. Además, esta notación reduce el número de procesos, no es probable que mida la diferencia en el rendimiento. Por lo tanto, podrías escribir sensatamente:

find . -exec grep something {} +

Versión clásica

find . -print | xargs grep something

Si estás en Linux o tienes GNU find y xargscomandos, use -print0con findy -0con xargspara manejar nombres de archivos que contienen espacios y otros caracteres extraños.

find . -print0 | xargs -0 grep something

Afinando los resultados de grep

Si no desea los nombres de archivo (solo el texto), agregue una opción adecuada grep(generalmente -hpara suprimir 'encabezados'). Para garantizar absolutamente que se imprima el nombre del archivo grep(incluso si solo se encuentra un archivo, o la última invocación de grepsolo se le da 1 nombre de archivo), luego agregue/dev/null a la xargslínea de comando, para que siempre haya al menos dos nombres de archivo.

Jonathan Leffler
fuente
Para aquellos confundidos como yo, tenga en cuenta que de esta manera primero dará toda la salida de find, y luego dará la salida de xargs grep something.
Eric Hu
3
@EricHu: Puedo ver que estás confundido, pero no hace lo que dices, al menos no en ningún sistema basado en Unix que yo sepa. La salida de findse canaliza a la entrada estándar de xargs. El xargsprograma lee su entrada estándar, divide la entrada en el espacio en blanco (espacios en blanco, líneas nuevas, pestañas, etc.) y agrega varias palabras al comando grep somethingy ejecuta la línea de comando. xargsluego continúa leyendo la entrada y ejecutando comandos hasta que se agota la entrada. xargsejecuta el grepcomando con la frecuencia necesaria para la entrada que se le da ( finden este ejemplo).
Jonathan Leffler
Ah, mi error, esto es usar grep para buscar dentro de cada archivo coincidente. Estaba buscando simplemente filtrar la salida de find con grep
Eric Hu
1
Los errores van al error estándar (descriptor de archivo 2) en todos los comandos con buen comportamiento. Redirigiendo stderr para /dev/nullperder los mensajes de error.
Jonathan Leffler
1
Esto también tiene la ventaja de que funciona mejor con espacios en la ruta del archivo. Incluso 'sed'ing "" -> "\" lo rompe con el `pero con xargs funciona perfectamente
JZL003
36

Hay algunas maneras de pasar la lista de archivos devueltos por el findcomando al catcomando, aunque técnicamente no todos usan tuberías, y ninguna de ellas se canaliza directamente cat.

  1. Lo más simple es usar backticks ( `):

    cat `find [whatever]`
    

    Esto toma el resultado findy lo coloca efectivamente en la línea de comando de cat. Esto no funciona bien si findtiene demasiada salida (más de la que cabe en una línea de comandos) o si la salida tiene caracteres especiales (como espacios).

  2. En algunos shells, incluidos bash, uno puede usar en $()lugar de backticks:

    cat $(find [whatever])
    

    Esto es menos portátil, pero es anidable. Aparte de eso, tiene casi las mismas advertencias que los backticks.

  3. Debido a que ejecutar otros comandos en lo que se encontró es un uso común find, find tiene una -execacción que ejecuta un comando para cada archivo que encuentra:

    find [whatever] -exec cat {} \;
    

    El {}es un marcador de posición para el nombre de archivo y \;marca el final del comando (es posible tener otras acciones después -exec).

    Esto se ejecutará catuna vez para cada archivo en lugar de ejecutar una sola instancia de catpasarle varios nombres de archivo que pueden ser ineficientes y pueden no tener el comportamiento que desea para algunos comandos (aunque está bien cat). La sintaxis también es incómoda de escribir: debe escapar del punto y coma porque el punto y coma es especial para el shell.

  4. Algunas versiones de find(sobre todo la versión de GNU) permiten sustituir ;con +usar -exec's añaden modo de correr menos casos de cat:

    find [whatever] -exec cat {} +
    

    Esto pasará múltiples nombres de archivo a cada invocación de cat, lo que puede ser más eficiente.

    Sin embargo, tenga en cuenta que esto no garantiza el uso de una sola invocación. Si la línea de comando fuera demasiado larga, los argumentos se distribuirían entre múltiples invocaciones de cat. Para catesto probablemente no es una gran cosa, pero para algunos otros comandos que esto puede cambiar el comportamiento de manera indeseable. En los sistemas Linux, el límite de longitud de la línea de comandos es bastante grande, por lo que la división en invocaciones múltiples es bastante rara en comparación con otros sistemas operativos.

  5. El enfoque clásico / portátil es usar xargs:

    find [whatever] | xargs cat
    

    xargsejecuta el comando especificado ( caten este caso) y agrega argumentos basados ​​en lo que lee de stdin. Al igual que -execcon +, esto dividirá la línea de comandos si es necesario. Es decir, si findproduce demasiada salida, se ejecutará catvarias veces. Como se mencionó en la sección -execanterior, hay algunos comandos donde esta división puede resultar en un comportamiento diferente. Tenga en cuenta que el uso xargscomo este tiene problemas con los espacios en los nombres de archivo, comoxargs solo utiliza espacios en blanco como delimitador.

  6. El método más robusto, portátil y eficiente también utiliza xargs:

    find [whatever] -print0 | xargs -0 cat
    

    La -print0bandera le dice findque use \0delimitadores (caracteres nulos) entre los nombres de archivo, y la -0bandera le dice xargsque espere estos \0delimitadores. Esto tiene un comportamiento bastante idéntico al -exec... +enfoque, aunque es más portátil (pero desafortunadamente más detallado).

Laurence Gonsalves
fuente
El método de retroceso es excelente, porque también funciona para otros comandos como ls.
Martin Braun
@Martin Braun usando $()También funciona con comandos distintos de find .
Laurence Gonsalves
Gracias, es bueno saber que dejé de leer después de (1), porque sirve a mis necesidades, ya que no estoy tratando con caracteres especiales como espacios y demás.
Martin Braun
9

Para lograr esto (usando bash) haría lo siguiente:

cat $(find . -name '*.foo')

Esto se conoce como la "sustitución de comandos" y elimina el avance de línea por defecto, ¡lo cual es realmente conveniente!

más información aquí

Stphane
fuente
6

A mí me parece un trabajo para un script de shell:

for file in 'find -name *.xml'
do
   grep 'hello' file
done

o algo así

Gandalf
fuente
2
Esta es una respuesta válida, aunque no necesariamente óptima, a la pregunta.
Jonathan Leffler
1
... sí, pero es genial si quieres un archivo grande con nombres de archivos listados también.
ʍǝɥʇɐɯ
1
Me gusta esto lo mejor. Un bloque de bucle como este deja espacio para hacer otras cosas.
kakyo
4

Esta es mi manera de encontrar nombres de archivos que contengan algún contenido que me interese, solo una línea bash que también maneja bien los espacios en los nombres de archivos:

find . -name \*.xml | while read i; do grep '<?xml' "$i" >/dev/null; [ $? == 0 ] && echo $i; done
Greg
fuente
3

Yo uso algo como esto:

find . -name <filename> -print0 | xargs -0 cat | grep <word2search4>

" -print0" argumento para "buscar" y " -0" argumento para "xargs" son necesarios para manejar los espacios en blanco en las rutas / nombres de archivo correctamente.

ekinak
fuente
2

Aquí está mi tiro para uso general:

grep YOURSTRING `find .`

Imprimirá el nombre del archivo

zakki
fuente
2

En bash, lo siguiente sería apropiado:

find /dir -type f -print0 | xargs -0i cat {} | grep whatever

Esto encontrará todos los archivos en el /dirdirectorio y canalizará los nombres de los archivos de xargsforma segura, lo que conducirá de manera segura grep.

Omitir xargsno es una buena idea si tiene muchos miles de archivos /dir; catse romperá debido a la longitud excesiva de la lista de argumentos. xargslo resolverá todo por ti.

El -print0argumento de findmallas con el -0argumento xargsde manejar los nombres de archivo con espacios correctamente. El -iargumento para le xargspermite insertar el nombre del archivo donde sea necesario en la catlínea de comando. Los corchetes se reemplazan por el nombre del archivo canalizado en el catcomando desde find.

McClain Looney
fuente
0

Esto funciona para mi

find _CACHE_* | while read line; do
    cat "$line" | grep "something"
done
Steven Penny
fuente
0

Usa ggrep .

ggrep -H -R -I "mysearchstring" *

para buscar un archivo en Unix que contenga texto ubicado en el directorio actual o un subdirectorio

Underverse
fuente
0

Esto imprimirá el nombre y el contenido de los archivos solo de forma recursiva.

find . -type f -printf '\n\n%p:\n' -exec cat {} \;

Editar (versión mejorada): Esto imprimirá el nombre y el contenido de los archivos de texto (ascii) solo de forma recursiva.

find . -type f -exec grep -Iq . {} \; -print | xargs awk 'FNR==1{print FILENAME ":" $0; }'

Un intento mas

find . -type f -exec grep -Iq . {} \; -printf "\n%p:" -exec cat {} \;
Adlinge prasante
fuente
-1

¿Estás tratando de encontrar texto en archivos? Simplemente puede usar grep para eso ...

grep searchterm *
Scott Anderson
fuente
-1

Para enumerar y ver el contenido de todos los archivos abc.def en un servidor en los directorios / ghi y / jkl

find /ghi /jkl -type f -name abc.def 2> /dev/null -exec ls {} \; -exec cat {} \;

Para enumerar los archivos abc.def que tienen entradas comentadas y mostrar, vea esas entradas en los directorios / ghi y / jkl

find /ghi /jkl -type f -name abc.def 2> /dev/null -exec grep -H ^# {} \;
Sharjeel
fuente