Wie kann ich den Inhalt von Dateien, die mit find gefunden wurden, in eine einzelne Datei zusammenfassen?

11

Ich habe es geschafft, mich dort zu fotografieren, wo es weh tut (wirklich schlimm), indem ich eine Partition neu formatiert habe, die wertvolle Daten enthielt. Natürlich war es nicht beabsichtigt, aber es ist passiert.

Es gelang mir jedoch, die meisten Daten zu verwenden testdiskund photorecwiederherzustellen. Jetzt habe ich all diese Daten auf fast 25.000 Verzeichnisse verteilt. Die meisten Dateien sind TXT-Dateien, während der Rest Bilddateien sind. In jedem Verzeichnis befinden sich mehr als 300 TXT-Dateien.

Ich kann grepoder kann findbestimmte Zeichenfolgen aus den TXT-Dateien extrahieren und in eine Datei ausgeben. In der folgenden Zeile habe ich beispielsweise überprüft, ob sich meine Daten in den wiederhergestellten Dateien befinden:

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

Ich kann "searchPattern" in eine Datei ausgeben, aber das gibt mir nur dieses Muster. Folgendes möchte ich wirklich erreichen:

Durchsuchen Sie alle Dateien und suchen Sie nach einer bestimmten Zeichenfolge. Wenn diese Zeichenfolge in einer Datei gefunden wird, ordnen Sie den gesamten Inhalt dieser Datei einer Ausgabedatei zu. Wenn das Muster in mehr als einer Datei gefunden wird, hängen Sie den Inhalt nachfolgender Dateien an diese Ausgabedatei an. Beachten Sie, dass ich nicht das gesuchte Muster ausgeben möchte, sondern den gesamten Inhalt der Datei, in der sich die Muster befinden.

Ich denke, das ist machbar, aber ich weiß einfach nicht, wie ich den gesamten Inhalt einer Datei abrufen soll, nachdem ich ein bestimmtes Muster daraus entnommen habe.

Ami
quelle
Mit dem von Ihnen angegebenen Befehl erhalten Sie die gewünschten Ergebnisse, möchten aber die Ausgabe in eine Textdatei umleiten?
Ryekayo
Nachdem ich meine Frage gelesen habe, klingt dieser Absatz, der mit "Durchgehen ..." beginnt, genau wie Pseudocode. Vielleicht kann ich es Code mit ein paar Zeilen für / wenn Python-Code bekommen. Ich werde es versuchen, während ich auf eine informierte Antwort
warte
Es ist sicherlich Pseudocode, und ich bin sicher, dass Sie einen Weg finden können, dies auch in Bash zu tun.
Ryekayo
@ryekayo, Ja, es gibt mir die Ausgabe, aber das ist nur, um herauszufinden, in welcher Datei sich ein bestimmter Datentyp befindet, was mir sagt, dass sich mehr dieser Daten in dieser Datei befinden. Also möchte ich alles in dieser Datei aufnehmen und in eine andere Datei schreiben.
Ami
Sie können diesen Befehl wahrscheinlich in eine if-Anweisung oder sogar in einen switch-case
einschließen

Antworten:

10

Wenn ich Ihr Ziel richtig verstehe, wird Folgendes tun, was Sie wollen:

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

Dies sucht nach allen *.txtDateien in ./recup*/, testet jede auf searchPattern, wenn es mit catder Datei übereinstimmt . Die Ausgabe aller cated-Dateien wird geleitet outputfile.txt.

Wiederholen Sie dies für jedes Muster und jede Ausgabedatei.


Wenn Sie eine sehr große Anzahl übereinstimmender Verzeichnisse haben ./recup*, erhalten Sie möglicherweise eine argument list too long error. Der einfache Weg, dies zu umgehen, besteht darin, stattdessen so etwas zu tun:

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

Dies entspricht dem vollständigen Pfad. Also ./recup01234/foo/bar.txtwird abgestimmt. Das -mindepth 2ist so, dass es nicht passt ./recup.txt, oder ./recup0.txt.

Patrick
quelle
Ja, ich denke das wird es tun. Und es gibt mir eine Basis, von der aus ich arbeiten kann. Da ich nach mehreren Zeichenfolgen suchen werde, denke ich, dass ein For / If-Code mit mehreren Elifs mir dabei helfen wird, die Aufgabe zu automatisieren. Vielen Dank
Ami
Das ist noch besser als ich dachte lol
ryekayo
Das schien nicht zu funktionieren. Ich habe diesen Fehler erhalten: "/ usr / bin / find kann nicht ausgeführt werden: Argumentliste zu lang"
Ami
@Ami hat die Antwort aktualisiert, um eine Lösung für dieses Problem bereitzustellen.
Patrick
2
@Ami Wenn Sie mehrere Strings verwenden, könnte es einfacher sein, nur auf eine andere Datei alle positiven Dateinamen zu speichern ( grep -l), dann |sort|uniqund cataus der Dateiliste.
Sparhawk
3

Anstatt Ihr Muster auszugeben, geben Sie den Dateinamen mit "-l" auf grep aus und verwenden Sie diesen dann als Eingabe für cat.

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

oder

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

Ich vermute, dass Sie die restlichen Details eintragen können. Übrigens, wenn Sie möglicherweise Leerzeichen oder andere ungerade Zeichen in den Dateinamen haben (in diesem speziellen Fall unwahrscheinlich, aber für zukünftige Zwecke), verwenden Sie -print0 für die Suche und -Z für das grep, kombiniert mit der Option -0 für die zu verwendenden xargs Null-Bytes zwischen Dateinamen und keine Zeilenumbrüche.

find ./recup*/ -name '*.txt' -print0 | xargs -0 grep -Zli "searchPattern" | xargs -0 cat
dannysauer
quelle
2
Ich mag auch Patricks "two -exec" -Option, außer dass sie für jede Datei einen neuen Fork (na ja, clone ()) und exec verursacht. Normalerweise können Sie dieses Problem \+eher verwenden als \;vermeiden, aber ich weiß nicht, wie das mit einem Paar von -exec-Argumenten funktioniert (ich vermute "schlecht"). Wenn Sie ein Paar xargs verwenden, werden nur ein paar neue Prozesse erzeugt, die mit vielen Dateien schneller sein sollten.
Dannysauer
Das sieht auch gut aus. Vielen Dank. Eine Noob-Frage: Die Katze nach den letzten Xargs sollte in eine Datei ausgegeben werden, oder?
Ami
Als ich es zum ersten Mal las, dachte ich nicht, dass die Frage spezifizierte, wohin der Inhalt der Datei gehen sollte. Alle drei dieser Befehle setzen die Datei (en) Inhalt auf STDOUT, so würden Sie gerade append (bis zum Ende) >afileoder |acommandoder was auch immer für Ihre Situation geeignet ist. :)
dannysauer
Gute Antwort, ich musste pg_hba.confsudo find /* -name pg_hba.conf | xargs sudo cat
App Work
Dies ist ein wenig abseits des Themas, aber ich bevorzuge die Verwendung sudo xargsanstelle von xargs sudo. Wenn Sie ausführen xargs sudo, wird die Befehlszeile unter der Annahme erstellt, dass der Befehl lautet sudo cat args. Aber Katze ist in / bin, also rennt sudo /bin/cat args. Befindet sich Ihr Befehl in einem längeren Verzeichnis wie / usr / local / bin, führt der tatsächlich ausgeführte Befehl sudo möglicherweise zu einer zu langen Befehlszeile und einem Fehler, der schwer zu finden ist. Darüber hinaus wird sudo xargsnur protokolliert, dass Sie xargs ausgeführt haben, während xargs sudoder Befehl mit allen Argumenten protokolliert wird - was zu einigen langen Sudo-Protokollzeilen führt. :)
dannysauer
1

Dies ist kein optimaler Code, aber er ist sehr einfach und funktioniert einwandfrei, wenn Effizienz kein Problem darstellt. Das Problem ist, dass die Dateien mehrmals durchsucht werden, auch wenn die Zeichenfolge bereits darin gefunden wurde.

Suchen Sie zunächst nach Ihren Zeichenfolgen und schreiben Sie die passenden Dateien in eine Liste.

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

Wiederholen Sie diesen Schritt bei searchPatternBedarf. Dies erzeugt eine Liste übereinstimmender Dateien unter /tmp/file_list.

Das Problem ist, dass diese Datei möglicherweise Duplikate enthält. Daher können wir die Duplikate durch ersetzen |sort|uniq. Das sortTeil platziert die Duplikate nebeneinander, so dass uniqsie entfernt werden können. Dann können Sie catdiese Dateien zusammen mit xargs(wobei jeder Dateiname durch Zeilenumbruch getrennt ist \n). Daher,

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

Im Gegensatz zu den anderen Antworten enthält diese zwei Schritte und eine temporäre Datei. Ich würde sie daher nur empfehlen, wenn Sie mehrere Muster finden müssen.

Sparhawk
quelle
0

Abhängig von Ihrer Shell und Umgebung könnten Sie so etwas (in 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)

Wenn Sie die Ergebnisse nach Mustern trennen möchten, können Sie dies in etwa ändern

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)
Steeldriver
quelle
Was macht das Bit nach "erledigt"? Was ich eigentlich gerne möchte, ist, diesen if-Block so zu ändern, dass Dateien, die ein übereinstimmendes Muster enthalten, in ein anderes geschrieben werden.
Ami
Es werden nur die gefundenen '.txt'-Dateien aufgelistet, die jeweils durch das Nullzeichen abgeschlossen werden (damit Dateinamen mit Leerzeichen und anderen Zeichen sicher sind). Die whileSchleife liest dann diese Liste und führt den grep/ bedingten catTeil aus.
Steeldriver
Wenn ich versuche, den Code auszuführen, erhalte ich den folgenden Fehler: ./recoverData.sh: Syntaxfehler: "(" unerwartet. Das kommt von den Klammern um den Befehl find
Ami
Welche Shell benutzt du? Die Syntax der Prozessersetzung ist spezifisch für Bash - daher meine Qualifikation "Abhängig von Ihrer Shell und Umgebung"
Steeldriver
1
Sie können die Befehle entweder direkt in einer interaktiven Bash-Shell ausführen oder in eine Datei einfügen #!/bin/bash, deren erste Zeile den Shebang enthält , ihn ausführbar machen chmod +x recoverData.shund mit ausführen ./recoverData.sh. Sie nicht verwenden , sh recoverData.shda /bin/shwahrscheinlich eine ist dashShell .
Steeldriver