Comment puis-je regrouper le contenu des fichiers trouvés en utilisant find dans un seul fichier?

11

J'ai réussi à me tirer là où ça fait mal (vraiment mal) en reformatant une partition qui contenait des données précieuses. Bien sûr, ce n'était pas intentionnel, mais c'est arrivé.

Cependant, j'ai réussi à utiliser testdisket photorecà récupérer la plupart des données. Alors maintenant, j'ai toutes ces données réparties sur près de 25 000 répertoires. La plupart des fichiers sont des fichiers .txt, tandis que les autres sont des fichiers image. Il y a plus de 300 fichiers .txt dans chaque répertoire.

Je peux grepou utiliser findpour extraire certaines chaînes des fichiers .txt et les exporter vers un fichier. Par exemple, voici une ligne que j'ai utilisée pour vérifier que mes données se trouvent dans les fichiers récupérés:

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

Je peux sortir "searchPattern" dans un fichier, mais cela me donne juste ce modèle. Voici ce que j'aimerais vraiment accomplir:

Parcourez tous les fichiers et recherchez une chaîne spécifique. Si cette chaîne se trouve dans un fichier, transférez TOUT le contenu de ce fichier dans un fichier de sortie. Si le modèle se trouve dans plusieurs fichiers, ajoutez le contenu des fichiers suivants à ce fichier de sortie. Notez que je ne veux simplement pas sortir le motif que je recherche, mais TOUT le contenu du fichier dans lequel les motifs sont trouvés.

Je pense que c'est faisable, mais je ne sais tout simplement pas comment récupérer tout le contenu d'un fichier après avoir récupéré un modèle spécifique.

Suis-je
la source
Donc, avec la commande que vous avez fournie, elle vous donne les résultats que vous recherchez mais vous cherchez à rediriger la sortie vers un fichier texte?
Ryekayo
Après avoir lu ma question, ce paragraphe qui commence par "Passez par ..." sonne comme un pseudo-code. Peut-être que je peux obtenir du code avec quelques lignes de code Python for / if. Je vais lui donner un coup de feu en attendant une réponse plus éclairée
Ami
C'est certainement un pseudo-code, et je suis sûr que vous pouvez également trouver un moyen de le faire en bash.
Ryekayo
@ryekayo, Oui, cela me donne la sortie, mais c'est juste pour trouver dans quel fichier se trouve un type spécifique de données, ce qui me dit que plus de ces données sont dans ce fichier. Je veux donc tout saisir dans ce fichier et les écrire dans un autre fichier.
Ami
Vous pouvez probablement envelopper cette commande dans une sorte d'instruction if ou même dans un commutateur qui peut appeler une fonction qui peut masquer le contenu en fonction de la casse ou des résultats de l'instruction if
ryekayo

Réponses:

10

Si je comprends bien votre objectif, ce qui suit fera ce que vous voulez:

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

Cela cherchera tous les *.txtfichiers dans ./recup*/, testez chacun d'eux searchPattern, s'il correspond, catle fichier. La sortie de tous les catfichiers ed sera dirigée vers outputfile.txt.

Répétez l'opération pour chaque modèle et fichier de sortie.


Si vous avez un très grand nombre de répertoires correspondant ./recup*, vous pourriez vous retrouver avec un argument list too long error. Le moyen le plus simple est de faire quelque chose comme ceci à la place:

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

Cela correspondra au chemin complet. Ainsi ./recup01234/foo/bar.txtsera apparié. Le -mindepth 2est pour qu'il ne corresponde pas ./recup.txt, ou ./recup0.txt.

Patrick
la source
Oui, je pense que ça suffira. Et cela me donne une base pour travailler. Puisque je vais rechercher plusieurs chaînes, je pense qu'un bit de code for / if, avec plusieurs elif m'aidera à automatiser la tâche. Merci
Ami
C'est encore mieux que ce que je pensais lol
ryekayo
Cela ne semblait pas fonctionner. Vous avez cette erreur: "impossible d'exécuter / usr / bin / find: la liste des arguments est trop longue"
Ami
@Ami a mis à jour la réponse pour fournir une solution à ce problème.
Patrick
2
@Ami Si vous utilisez plusieurs chaînes, il pourrait être plus simple d'enregistrer simplement tous les noms de fichiers positifs à un autre fichier ( grep -l), puis |sort|uniqet catde la liste des fichiers.
Sparhawk
3

Plutôt que de sortir votre modèle, sortez le nom de fichier en utilisant "-l" sur grep, puis utilisez-le comme entrée pour cat.

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

ou

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

Je soupçonne que vous pouvez remplir les détails restants. BTW, si vous pouvez avoir des espaces ou d'autres caractères impairs dans les noms de fichiers (peu probable dans ce cas spécifique, mais à des fins futures), utilisez -print0 sur la recherche et -Z sur le grep, combiné avec l'option -0 sur xargs à utiliser octets nuls entre les noms de fichiers plutôt que les sauts de ligne.

find ./recup*/ -name '*.txt' -print0 | xargs -0 grep -Zli "searchPattern" | xargs -0 cat
dannysauer
la source
2
J'aime aussi l'option "two -exec" de Patrick, sauf qu'elle provoquera un nouveau fork (enfin, clone ()) et un exec pour chaque fichier. Normalement, vous pouvez utiliser \+plutôt que \;d'éviter ce problème, mais je ne sais pas comment cela fonctionne avec une paire d'arguments -exec (je soupçonne "mal"). En utilisant une paire de xargs, vous n'aurez que quelques nouveaux processus générés, ce qui devrait être plus rapide avec beaucoup de fichiers.
dannysauer
Cela semble bien aussi. Merci. Une seule question: le chat après les derniers xargs devrait sortir dans un fichier, non?
Ami
Quand je l'ai lu pour la première fois, je ne pensais pas que la question spécifiait où le contenu du fichier devait aller. Tous les trois de ces commandes placez le fichier (s) contenu sur STDOUT, donc vous devriez juste append (jusqu'à la fin) >afileou |acommandou tout ce qui est approprié pour votre situation. :)
dannysauer
Bonne réponse, je devais cat pg_hba.conf sudo find /* -name pg_hba.conf | xargs sudo cat
App Work
C'est un peu hors sujet, mais je préfère utiliser à la sudo xargsplace de xargs sudo. Lorsque vous exécutez xargs sudo, il crée la ligne de commande en supposant que la commande est sudo cat args. Mais cat est dans / bin, alors sudo s'exécute /bin/cat args. Si votre commande se trouve dans un répertoire plus long, comme / usr / local / bin, alors la commande sudo s'exécute réellement peut entraîner une ligne de commande trop longue et une erreur qui est difficile à localiser. En plus de cela, sudo xargsenregistre simplement que vous avez exécuté xargs, tandis que xargs sudoenregistre la commande avec tous les arguments - résultant en de longues lignes de journal sudo. :)
dannysauer
1

Ce n'est pas exactement du code optimal, mais il est très simple et fonctionnera bien si l'efficacité n'est pas un problème. Le problème est qu'il va parcourir les fichiers plusieurs fois, même si la chaîne y a déjà été trouvée.

Tout d'abord, recherchez vos chaînes et écrivez les fichiers correspondants dans une liste.

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

Répétez cette étape en remplaçant searchPatternsi nécessaire. Cela produit une liste de fichiers correspondants sur /tmp/file_list.

Le problème est que ce fichier peut contenir des doublons. Par conséquent, nous pouvons remplacer les doublons par |sort|uniq. La sortpièce place les doublons les uns à côté des autres, afin de uniqpouvoir les supprimer. Ensuite, vous pouvez catregrouper ces fichiers en utilisant xargs(chaque nom de fichier étant séparé par une nouvelle ligne \n). Par conséquent,

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

Contrairement aux autres réponses, cela comporte deux étapes et un fichier temporaire, donc je ne le recommanderais vraiment que si vous avez plusieurs modèles à trouver.

Sparhawk
la source
0

Selon votre shell et votre environnement, vous pourriez donc quelque chose comme ça (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 vous souhaitez séparer les résultats selon le modèle, vous pouvez le modifier en quelque chose comme

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)
tournevis
la source
Que fait le bit après "fait"? Ce que j'aimerai en fait, c'est de modifier ce bloc if pour que les fichiers qui contiennent un modèle correspondant soient écrits dans un autre.
Ami
Il répertorie simplement les fichiers '.txt' qui sont trouvés, chacun étant terminé par le caractère nul (de sorte qu'il est sûr pour les noms de fichiers contenant des espaces et d'autres caractères). La whileboucle lit ensuite cette liste et effectue la partie grep/ conditionnelle cat.
steeldriver
Lorsque j'essaie d'exécuter le code, j'obtiens cette erreur: ./recoverData.sh: Erreur de syntaxe: "(" inattendu. Cela vient des crochets autour de la commande find
Ami
Quel shell utilisez-vous? la syntaxe de substitution de processus est spécifique à bash - d'où ma qualification "En fonction de votre shell et de votre environnement"
steeldriver
1
Vous pouvez soit exécuter la ou les commandes directement dans un shell bash interactif, soit les placer dans un fichier dont la première ligne contient le shebang #!/bin/bash, le rendre exécutable avec chmod +x recoverData.shet l'exécuter avec ./recoverData.sh. Ne pas utiliser sh recoverData.shcar il /bin/shs'agit probablement d'un dashshell .
steeldriver