Jak mogę połączyć zawartość znalezionych plików za pomocą funkcji find w jednym pliku?

11

Udało mi się zastrzelić w miejscu, w którym boli (naprawdę źle), zmieniając format partycji zawierającej cenne dane. Oczywiście nie było to zamierzone, ale się stało.

Udało mi się jednak wykorzystać testdiski photorecodzyskać większość danych. Więc teraz mam wszystkie te dane rozproszone w prawie 25 000 katalogów. Większość plików to pliki .txt, a pozostałe to pliki obrazów. W każdym katalogu znajduje się ponad 300 plików .txt.

Mogę greplub mogę użyć finddo wyodrębnienia niektórych ciągów z plików .txt i przesłania ich do pliku. Na przykład oto wiersz, którego użyłem do sprawdzenia, czy moje dane znajdują się w odzyskanych plikach:

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

Mogę wypisać „searchPattern” do pliku, ale to tylko daje mi ten wzór. Oto, co naprawdę chciałbym osiągnąć:

Przejrzyj wszystkie pliki i poszukaj określonego ciągu. Jeśli ten ciąg zostanie znaleziony w pliku, cat CAŁĄ zawartość tego pliku do pliku wyjściowego. Jeśli wzorzec znajduje się w więcej niż jednym pliku, dołącz zawartość kolejnych plików do tego pliku wyjściowego. Zauważ, że po prostu nie chcę wypisywać szukanego wzorca, ale CAŁĄ zawartość pliku, w którym znaleziono wzorce.

Myślę, że jest to wykonalne, ale po prostu nie wiem, jak pobrać całą zawartość pliku po wytłoczeniu z niego określonego wzorca.

Jestem
źródło
Więc dzięki podanemu poleceniu daje wyniki, których szukasz, ale chcesz przekierować dane wyjściowe do pliku tekstowego?
ryekayo
Po przeczytaniu mojego pytania ten akapit, który zaczyna się od „Przejdź przez ...”, brzmi jak kod psuedocode. Może uda mi się uzyskać kod z kilkoma liniami kodu for / if Python. Dam mu szansę, czekając na bardziej świadomą odpowiedź
Ami
Z pewnością jest to kod psued i jestem pewien, że możesz znaleźć sposób na zrobienie tego również w bashu.
ryekayo
@ryekayo, tak, daje mi wynik, ale to tylko po to, aby znaleźć plik, w którym znajduje się określony typ danych, co mówi mi, że więcej tych danych znajduje się w tym pliku. Chcę więc pobrać wszystko z tego pliku i zapisać je w innym pliku.
Ami
Prawdopodobnie możesz zawinąć to polecenie w jakąś instrukcję if lub nawet w skrzynkę przełączającą, która może wywoływać funkcję, która może wyodrębnić zawartość na podstawie przypadku lub wyników instrukcji if
ryekayo

Odpowiedzi:

10

Jeśli poprawnie zrozumiem twój cel, następujące czynności zrobią, co chcesz:

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

Spowoduje to wyszukanie wszystkich *.txtplików ./recup*/, przetestowanie każdego z nich searchPattern, jeśli pasuje, catplik będzie. Dane wyjściowe wszystkich catplików ed zostaną skierowane outputfile.txt.

Powtórz dla każdego wzoru i pliku wyjściowego.


Jeśli masz bardzo dużo pasujących katalogów ./recup*, możesz skończyć na argument list too long error. Prostym sposobem jest zrobienie czegoś takiego:

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

To dopasuje pełną ścieżkę. Tak ./recup01234/foo/bar.txtbędzie dopasowane. -mindepth 2Jest tak, że nie będzie pasował ./recup.txt, albo ./recup0.txt.

Patrick
źródło
Tak, myślę, że to zrobi. I daje mi podstawę do pracy. Ponieważ będę szukał wielu ciągów znaków, myślę, że fragment kodu for / if z wieloma elifami pomoże mi zautomatyzować zadanie. Dziękuję
Ami
To nawet lepsze niż to, co myślałem lol
ryekayo
To nie działało. Wystąpił błąd: „nie można wykonać / usr / bin / find: lista argumentów za długa”
Ami
@Ami zaktualizowała odpowiedź, aby zapewnić rozwiązanie tego problemu.
Patrick
2
@Ami Jeśli używasz wielu ciągów, może to być prostsze po prostu zapisać wszystkie pozytywne nazwy plików do innego pliku ( grep -l), a następnie |sort|uniqi catz listy plików.
Sparhawk
3

Zamiast wypisywać wzorzec, wypisz nazwę pliku używając „-l” na grep, a następnie użyj go jako danych wejściowych do cat.

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

lub

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

Podejrzewam, że możesz wypełnić pozostałe dane. BTW, jeśli możesz mieć spacje lub inne nieparzyste znaki w nazwach plików (mało prawdopodobne w tym konkretnym przypadku, ale do przyszłych celów), użyj -print0 na znalezieniu i -Z na grep, w połączeniu z opcją -0 na xargs, aby użyć puste bajty między nazwami plików, a nie nowymi liniami.

find ./recup*/ -name '*.txt' -print0 | xargs -0 grep -Zli "searchPattern" | xargs -0 cat
dannysauer
źródło
2
Podoba mi się również opcja „two -exec” Patricka, z tą różnicą, że spowoduje to nowe rozwidlenie (cóż, klonowanie) i wykonanie dla każdego pliku. Zwykle możesz użyć \+zamiast \;tego uniknąć, ale nie wiem, jak to działa z parą argumentów -exec (podejrzewam, że „źle”). Używając pary xargów, pojawi się tylko kilka nowych procesów, które powinny być szybsze przy dużej liczbie plików.
dannysauer
To też dobrze wygląda. Dzięki. Jedno pytanie noob: kot po ostatnich xargs powinien wypisywać do pliku, prawda?
Ami
Kiedy po raz pierwszy go przeczytałem, nie sądziłem, że pytanie określa, gdzie powinna znajdować się zawartość pliku. Wszystkie trzy z tych poleceń umieścić plik (i) zawartość na standardowe wyjście, więc że tylko append (do samego końca) >afilelub |acommandczy coś jest właściwe dla danej sytuacji. :)
dannysauer
Dobra odpowiedź, potrzebowałem cat pg_hba.confsudo find /* -name pg_hba.conf | xargs sudo cat
App Work
To trochę nie na temat, ale wolę używać sudo xargszamiast xargs sudo. Po uruchomieniu xargs sudobuduje linię poleceń, zakładając, że jest to polecenie sudo cat args. Ale cat jest w / bin, więc sudo działa /bin/cat args. Jeśli twoje polecenie znajduje się w dłuższym katalogu, takim jak / usr / local / bin, wówczas polecenie sudo faktycznie uruchomione może spowodować zbyt długi wiersz polecenia i błąd, który jest trudny do wyśledzenia. Poza tym sudo xargspo prostu loguje, że uruchomiłeś xargs, a xargs sudologuje komendę ze wszystkimi argumentami - co powoduje powstanie długich linii dziennika sudo. :)
dannysauer
1

To nie jest dokładnie optymalny kod, ale jest bardzo prosty i będzie działał dobrze, jeśli wydajność nie stanowi problemu. Problem polega na tym, że wielokrotnie przeszukuje pliki, nawet jeśli łańcuch już w nich został znaleziony.

Po pierwsze, wyszukaj swoje ciągi i zapisz pasujące pliki na liście.

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

Powtórz ten krok, zastępując searchPatternw razie potrzeby. To tworzy listę pasujących plików w /tmp/file_list.

Problem polega na tym, że ten plik może mieć duplikaty. Dlatego możemy zastąpić duplikaty |sort|uniq. sortCzęść umieszcza duplikaty przylegające do siebie, tak że uniqmożna je usunąć. Następnie możesz catpołączyć te pliki razem za pomocą xargs(z każdą nazwą pliku oddzieloną znakiem nowej linii \n). W związku z tym,

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

W przeciwieństwie do innych odpowiedzi zawiera on dwa kroki i plik tymczasowy, więc naprawdę polecam go tylko wtedy, gdy masz wiele wzorców do znalezienia.

Krogulec
źródło
0

W zależności od powłoki i środowiska możesz zrobić coś takiego (w 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)

Jeśli chcesz oddzielić wyniki według wzorca, możesz to zmienić na coś podobnego

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
źródło
Co robi bit po „zakończeniu”? To, co naprawdę mi się podoba, to zmodyfikować to, jeśli blok, tak aby pliki zawierające dopasowany wzorzec były zapisywane w innym.
Ami
Po prostu wyświetla listę znalezionych plików „.txt”, z których każdy jest zakończony znakiem null (dzięki czemu jest bezpieczny dla nazw plików zawierających spacje i inne znaki). whilePętli odczytuje, że listy i robi grep/ warunkowy catudział.
steeldriver
Gdy próbuję uruchomić kod, pojawia się następujący błąd: ./recoverData.sh: Błąd składniowy: „(„ nieoczekiwany. Pochodzi z nawiasów wokół polecenia find
Ami
Jakiej powłoki używasz? składnia podstawiania procesów jest specyficzna dla bash - stąd moja kwalifikacja „W zależności od twojej powłoki i środowiska”
steeldriver
1
Możesz albo wykonać polecenie (polecenia) bezpośrednio w interaktywnej powłoce bash, albo umieścić je w pliku, którego pierwszy wiersz zawiera shebang #!/bin/bash, uczynić go wykonywalnym za chmod +x recoverData.shpomocą ./recoverData.sh. Czy nie używać sh recoverData.sh, ponieważ /bin/shjest prawdopodobne, dashskorupa .
steeldriver