¿Cómo puedo capturar el contenido de los archivos encontrados usando find en un solo archivo?

11

Me las arreglé para pegarme un tiro donde duele (realmente mal) reformateando una partición que contenía datos valiosos. Por supuesto que no fue intencional, pero sucedió.

Sin embargo, logré usar testdisky photorecrecuperar la mayoría de los datos. Así que ahora tengo todos esos datos distribuidos en casi 25,000 directorios. La mayoría de los archivos son archivos .txt, mientras que el resto son archivos de imagen. Hay más de 300 archivos .txt en cada directorio.

Puedo grepo uso findextraer ciertas cadenas de los archivos .txt y enviarlas a un archivo. Por ejemplo, aquí hay una línea que he usado para verificar que mis datos están en los archivos recuperados:

find ./recup*/ -name '*.txt' -print | xargs grep -i "searchPattern"

Puedo enviar "searchPattern" a un archivo, pero eso solo me da ese patrón. Esto es lo que realmente me gustaría lograr:

Revisa todos los archivos y busca una cadena específica. Si esa cadena se encuentra en un archivo, cat TODOS los contenidos de ese archivo en un archivo de salida. Si el patrón se encuentra en más de un archivo, agregue el contenido de los archivos posteriores a ese archivo de salida. Tenga en cuenta que simplemente no quiero generar el patrón que estoy buscando, sino TODOS los contenidos del archivo en el que se encuentran los patrones.

Creo que esto es factible, pero simplemente no sé cómo tomar todo el contenido de un archivo después de extraer un patrón específico de él.

Ami
fuente
Entonces, con el comando que proporcionó, le brinda los resultados que está buscando pero que desea redirigir la salida a un archivo de texto.
ryekayo
Después de leer mi pregunta, ese párrafo que comienza con "Ir a través de ..." suena como psuedocode. Tal vez pueda obtener el código con algunas líneas de código Python for / if. Lo intentaré mientras espero una respuesta más informada
Ami
Ciertamente es psuedocode, y estoy seguro de que también puedes encontrar una manera de hacerlo en bash.
ryekayo
@ryekayo, Sí, me da la salida, pero eso es solo para encontrar en qué archivo se encuentra un tipo específico de datos, lo que me dice que hay más de esos datos en ese archivo. Así que quiero tomar todo en ese archivo y escribirlos en otro archivo.
Ami
Probablemente pueda envolver ese comando en algún tipo de declaración if o incluso un caso de cambio que pueda llamar a una función que pueda capturar el contenido en función del caso o los resultados de la declaración if
ryekayo

Respuestas:

10

Si entiendo tu objetivo correctamente, lo siguiente hará lo que quieras:

find ./recup*/ -name '*.txt' -exec grep -qi "searchPattern" {} \; -exec cat {} \; > outputfile.txt

Esto buscará todos los *.txtarchivos ./recup*/, probará cada uno searchPattern, si coincide, aparecerá catel archivo. La salida de todos los catarchivos ed se dirigirá a outputfile.txt.

Repita para cada patrón y archivo de salida.


Si tiene una gran cantidad de directorios coincidentes ./recup*, puede terminar con un argument list too long error. La forma simple de evitar esto es hacer algo como esto:

find ./ -mindepth 2 -path './recup*.txt' -exec grep -qi "searchPattern" {} \; -exec cat {} \; > outputfile.txt

Esto coincidirá con la ruta completa. Entonces ./recup01234/foo/bar.txtserá igualado. El -mindepth 2es para que no coincida ./recup.txt, o ./recup0.txt.

Patricio
fuente
Sí, creo que eso lo hará. Y me da una base para trabajar. Como voy a buscar varias cadenas, creo que un código de código for / if, con múltiples elif me ayudará a automatizar la tarea. Gracias
Ami
Eso es incluso mejor de lo que estaba pensando jajaja
ryekayo
Eso no pareció funcionar. Recibió este error: "no se puede ejecutar / usr / bin / find: la lista de argumentos es demasiado larga"
Ami
Respuesta actualizada de @Ami para proporcionar una solución a ese problema.
Patrick
2
@Ami Si está utilizando varias cadenas, puede ser más sencillo guardar todos los nombres de archivo positivos en otro archivo ( grep -l), luego |sort|uniqy catdesde la lista de archivos.
Sparhawk
3

En lugar de mostrar su patrón, envíe el nombre de archivo usando "-l" en grep, y luego úselo como entrada para cat.

find ./recup*/ -name '*.txt' -print | xargs grep -li "searchPattern" | xargs cat

o

cat $( find ./recup*/ -name '*.txt' -print | xargs grep -li "searchPattern")

Sospecho que puede completar los detalles restantes. Por cierto, si puede tener espacios u otros caracteres extraños en los nombres de archivo (poco probable en este caso específico, pero para fines futuros), use -print0 en el hallazgo y -Z en el grep, combinado con la opción -0 en xargs para usar bytes nulos entre nombres de archivo en lugar de líneas nuevas.

find ./recup*/ -name '*.txt' -print0 | xargs -0 grep -Zli "searchPattern" | xargs -0 cat
dannysauer
fuente
2
También me gusta la opción "dos-exec" de Patrick, excepto que causará una nueva bifurcación (bueno, clone ()) y exec para cada archivo. Normalmente puede usar en \+lugar de \;evitar ese problema, pero no sé cómo funciona con un par de argumentos -exec (sospecho que "mal"). Usando un par de xargs, solo tendrás un par de nuevos procesos generados, que deberían ser más rápidos con muchos archivos.
dannysauer
Esto también se ve bien. Gracias. Una pregunta novata: el gato después de los últimos xargs debería estar dando salida a un archivo, ¿verdad?
Ami
Cuando lo leí por primera vez, no pensé que la pregunta especificara dónde debería ir el contenido del archivo. Los tres de estos comandos poner el contenido del archivo (s) en la salida estándar, por lo que acababa de adición (hasta el final) >afileo |acommando lo que sea apropiado para su situación. :)
dannysauer
Buena respuesta, necesitaba un gato pg_hba.conf sudo find /* -name pg_hba.conf | xargs sudo cat
App Work
Esto es un poco fuera de tema, pero prefiero usar en sudo xargslugar de xargs sudo. Cuando ejecuta xargs sudo, construye la línea de comando asumiendo que el comando es sudo cat args. Pero cat está en / bin, entonces sudo corre /bin/cat args. Si su comando está en un directorio más largo, como / usr / local / bin, entonces el comando sudo realmente se ejecuta podría resultar en una línea de comando demasiado larga y un error que es difícil de rastrear. Además de eso, sudo xargssolo registra que ejecutó xargs, mientras que xargs sudoregistra el comando con todos los argumentos, lo que resulta en algunas largas líneas de registro de sudo. :)
dannysauer
1

Este no es exactamente el código óptimo, pero es muy sencillo y funcionará bien si la eficiencia no es un problema. El problema es que examinará los archivos varias veces, incluso si la cadena ya se ha encontrado en ellos.

En primer lugar, busque sus cadenas y escriba los archivos coincidentes en una lista.

find ./recup*/ -name '*.txt' -execdir grep -il "searchPattern" {} >> /tmp/file_list \;

Repita este paso reemplazando searchPatternsegún sea necesario. Esto produce una lista de archivos coincidentes en /tmp/file_list.

El problema es que este archivo puede tener duplicados. Por lo tanto, podemos reemplazar los duplicados con |sort|uniq. La sortparte coloca los duplicados adyacentes entre sí, para que uniqpueda eliminarlos. Luego puede catusar estos archivos juntos xargs(con cada nombre de archivo separado por nueva línea \n). Por lo tanto,

</tmp/file_list sort | uniq | xargs -d "\n" cat > final_file.txt

A diferencia de las otras respuestas, esto tiene dos pasos y un archivo temporal, por lo que realmente solo lo recomendaría si tiene múltiples patrones para encontrar.

Gavilán
fuente
0

Dependiendo de su shell y entorno, podría hacer algo como esto (en bash)

while IFS= read -r -d '' file; do
  if grep -qim1 'searchPattern1\|searchPattern2\|searchPattern3' "$file"; then
    cat "$file" >> some/other/file
  fi
done < <(find ./recup*/ -name '*.txt' -print0)

Si desea separar los resultados de acuerdo con el patrón, puede modificarlo a algo como

while IFS= read -r -d '' file; do
  if grep -qim1 'searchPattern1' "$file"; then
    cat "$file" >> some/other/file1
  elif grep -qim1 'searchPattern2' "$file"; then
    cat "$file" >> some/other/file2
  elif grep -qim1 'searchPattern3' "$file"; then
    cat "$file" >> some/other/file3
  fi
done < <(find ./recup*/ -name '*.txt' -print0)
conductor de acero
fuente
¿Qué hace el bit después de "hecho"? Lo que realmente me gustaría es modificar ese bloque if para que los archivos que contienen un patrón coincidente se escriban en otro.
Ami
Simplemente enumera los archivos '.txt' que se encuentran, cada uno terminado por el carácter nulo (de modo que sea seguro para los nombres de archivo que contienen espacios y otros caracteres). El whilebucle lee entonces que las listas y hace el grep/ condicional catparte.
steeldriver
Cuando intento ejecutar el código, aparece este error: ./recoverData.sh: Error de sintaxis: "(" inesperado. Eso viene de los corchetes alrededor del comando find
Ami
¿Qué caparazón estás usando? la sintaxis de sustitución proceso es específico de fiesta - de ahí mi calificación "Dependiendo de su cáscara y el medio ambiente"
steeldriver
1
Puede ejecutar los comandos directamente en un shell bash interactivo o ponerlos en un archivo cuya primera línea contenga el shebang #!/bin/bash, hacerlo ejecutable chmod +x recoverData.shy ejecutarlo usando ./recoverData.sh. No lo use sh recoverData.shya que /bin/shes probable que sea una dashcáscara .
steeldriver