Znajdź ostatnie wystąpienie ciągu w wielu plikach


9

Muszę przeszukać wiele plików dziennika (wszystkie pliki wygenerowane w ciągu ostatnich 24 godzin, wszystkie przechowywane w tym samym katalogu), aby znaleźć ostatnie wystąpienie ciągu. Oto polecenie, które napisałem:

find . -mtime 1 | grep fileprefix | xargs grep 'search string' | tail -1

Ale to zwraca tylko ostatnią linię dla jednego pliku. Wszelkie sugestie, jak to zmienić, aby uzyskać wszystkie linie?


próbowałeś odwrócić ogon i ostatnie grep? odnaleźć . -mtime 1 | grep fileprefix | xargs tail -1 | grep 'search string'
— Mathieu,

Odpowiedzi:


4

Zakładając, że obiekty GNU:

find . -mtime -1 -exec bash -c \
'for f; do tac "$f" | grep -m1 fileprefix; done' _ {} +

Czy możesz proszę opracować cel „bash -c \”, ponieważ już używam powłoki bash. Również cel „_ {} +” na końcu.
— Lokesh,

@Lokesh, możesz dostać się finddo wykonywania poleceń na plikach za pomocą -exec. Dzięki bash -c, jesteśmy tarła bashskorupę że pętle za pośrednictwem plików znaleźć findi wykonuje tac .. | grep -m1 fileprefixna każdym
— Iruvar

Próbowałem rozszerzyć filtrowanie ciągu znaków w pętli za pomocą polecenia cut tj. Dla f; do tac "$ f" | grep -m1 poprawka pliku | cut -d '' -f4,7-8, ale gdy wstawię polecenie cut, daje mi błąd nieoczekiwany koniec pliku. Czy możesz mi zasugerować, co robię źle.
— Lokesh,

@lokesh, użyj -d" "z cięciem. Podwójne cudzysłowy zamiast pojedynczego
— iruvar

1
findPolecenia można filtrować za prefiksu pliku; grepnie powinno być potrzebne do tego. Zaskakujące jest również to, że szukany ciąg nie figuruje w tej odpowiedzi.
— Jonathan Leffler,

8

Jeśli wszystko jest w jednym katalogu, możesz:

for file in *fileprefix*; do
    grep 'search string' "$file" | tail -1
done

Jeśli są to duże pliki, warto przyspieszyć, tacdrukując plik w odwrotnej kolejności (ostatni wiersz), a następnie grep -m1dopasowując do pierwszego wystąpienia. W ten sposób unikniesz konieczności czytania całego pliku:

for file in *fileprefix*; do
    tac file | grep -m1 'search string'
done

Oba zakładają, że nie ma pasujących katalogów fileprefix. Jeśli tak, pojawi się błąd, który możesz po prostu zignorować. Jeśli to jest problem, sprawdź tylko pliki:

 for file in *fileprefix*; do
    [ -f "$file" ] && tac file | grep -m1 'search string'
 done

Jeśli potrzebujesz także wydrukować nazwę pliku, dodaj -Hdo każdego grepwywołania. Lub, jeśli grepnie obsługuje tego, powiedz mu, aby przeszukać /dev/null. To nie zmieni danych wyjściowych, ale ponieważ greppodano wiele plików, zawsze będzie drukować nazwę pliku dla każdego trafienia:

for file in *fileprefix*; do
    grep 'search string' "$file" /dev/null | tail -1
done

„W ten sposób unikasz konieczności czytania całego pliku” - co? Nie, unikasz czytania całego pliku w grep, ale zamiast tego wkładasz cały plik przez tac. Nie jest dla mnie jasne, czy byłoby to szybsze, choć zależałoby to od tego, czy dopasowanie było blisko początku czy końca pliku.
— Gilles „SO- przestań być zły”

@Gilles nie, nie przejdziesz też całego pliku tac. Wyjdzie, gdy tylko pierwszy mecz zostanie znaleziony. Właśnie przetestowałem z plikiem tekstowym 832M i wzorem znalezionym w ostatnim wierszu. grep -m 1 pattern filenarzędzie ~ 7 sekund i tac file | grep -m1 patternzajęło 0.009.
— terdon

4
find . ! -name . -prune -mtime 1 -name 'fileprefix*' \
     -exec sed -se'/searchstring/h;$!d;x' {} +

... będzie działać, jeśli masz GNU, sedktóry obsługuje -sopcję oddzielnych plików i POSIX find.

Prawdopodobnie powinieneś jednak dodać ! -type dlub -type fkwalifikatory, ponieważ próba odczytania katalogu nie będzie bardzo przydatna, a dalsze zawężenie zakresu do zwykłych plików może zapobiec zawieszeniu odczytu na potoku lub pliku urządzenia szeregowego.

Logika jest niezwykle prosta - sedzastępuje swoją hstarą przestrzeń kopią dowolnego pasującego wiersza wejściowego searchstring, a następnie dusuwa z wyjścia wszystkie wiersze wejściowe, ale ostatnie dla każdego pliku wejściowego. Gdy dojdzie do ostatniego wiersza, xzmienia przestrzenie wstrzymania i wzorców, więc jeśli searchstringw ogóle zostanie znalezione podczas odczytu pliku, ostatnie takie wystąpienie zostanie automatycznie wydrukowane na wyjście, w przeciwnym razie zapisze pustą linię. (dodaj /./!dto do końca sedskryptu, jeśli jest to niepożądane) .

Spowoduje to wykonanie pojedynczego sedwywołania dla około 65 000 plików wejściowych - lub dowolnego ARG_MAXlimitu. To powinno być bardzo wydajne rozwiązanie i jest po prostu zaimplementowane.

Jeśli chcesz także nazwy plików, biorąc pod uwagę najnowszy GNU sed, możesz zapisać je w osobnych wierszach za pomocą Fpolecenia, albo możesz wydrukować je findna osobnej liście dla każdej partii, dodając -printgłówną po +.


1

Co powiesz na:

find . -mtime -1 -name "fileprefix*" -exec sh -c \
'echo "$(grep 'search string' $1 | tail -n 1),$1"' _ {} \;

Powyższe daje ładne wyjście z ostatnim wystąpieniem ciągu wyszukiwania w każdym pliku, po którym następuje nazwa pliku po przecinku (zmodyfikuj część „, $ 1” pod echem, aby zmienić formatowanie lub usunąć, jeśli nie jest to konieczne). Przykładowe dane wyjściowe, które wyszukują ciąg „10” w plikach z prefiksem nazwy „plik”, są następujące:

[dmitry@localhost sourceDir]$ find . -mtime -1 -name "file*" -exec  sh -c 'echo "$(grep '10' $1 | tail -n 1),$1"' _ {} \;
Another data 02 10,./file02.log
Some data 01 10,./file01.log
Yet another data 03 10,./file03.log 

1
find . -mtime 1 -name 'fileprefix*' -exec grep -Hn 'search string' {} + |
    sort -t: -k1,2 -n | 
    awk -F: '{key=$1 ; $1="" ; $2="" ; gsub(/^  /,"",$0); a[key]=$0} 
             END {for (key in a) { print key ":" a[key] }}'

Wykorzystuje GNU grep„s -Hi -nopcje, aby zawsze wydrukować zarówno nazwę pliku i LineNumber wszystkich meczów, następnie sortuje według nazwy pliku i LineNumber i rury go do awk, który przechowuje ostatni mecz dla każdego pliku w tablicy, a ostatecznie drukuje to.

Metoda dość brutalna, ale działa.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.