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?
bash
shell-script
text-processing
grep
Lokesh
źródło
źródło
Odpowiedzi:
Zakładając, że obiekty GNU:
źródło
find
do wykonywania poleceń na plikach za pomocą-exec
. Dziękibash -c
, jesteśmy tarłabash
skorupę że pętle za pośrednictwem plików znaleźćfind
i wykonujetac .. | grep -m1 fileprefix
na każdym-d" "
z cięciem. Podwójne cudzysłowy zamiast pojedynczegofind
Polecenia można filtrować za prefiksu pliku;grep
nie powinno być potrzebne do tego. Zaskakujące jest również to, że szukany ciąg nie figuruje w tej odpowiedzi.Jeśli wszystko jest w jednym katalogu, możesz:
Jeśli są to duże pliki, warto przyspieszyć,
tac
drukując plik w odwrotnej kolejności (ostatni wiersz), a następniegrep -m1
dopasowując do pierwszego wystąpienia. W ten sposób unikniesz konieczności czytania całego pliku: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:Jeśli potrzebujesz także wydrukować nazwę pliku, dodaj
-H
do każdegogrep
wywołania. Lub, jeśligrep
nie obsługuje tego, powiedz mu, aby przeszukać/dev/null
. To nie zmieni danych wyjściowych, ale ponieważgrep
podano wiele plików, zawsze będzie drukować nazwę pliku dla każdego trafienia:źródło
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 file
narzędzie ~ 7 sekund itac file | grep -m1 pattern
zajęło0.009
.... będzie działać, jeśli masz GNU,
sed
który obsługuje-s
opcję oddzielnych plików i POSIXfind
.Prawdopodobnie powinieneś jednak dodać
! -type d
lub-type f
kwalifikatory, 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 -
sed
zastępuje swojąh
starą przestrzeń kopią dowolnego pasującego wiersza wejściowegosearchstring
, a następnied
usuwa z wyjścia wszystkie wiersze wejściowe, ale ostatnie dla każdego pliku wejściowego. Gdy dojdzie do ostatniego wiersza,x
zmienia przestrzenie wstrzymania i wzorców, więc jeślisearchstring
w ogóle zostanie znalezione podczas odczytu pliku, ostatnie takie wystąpienie zostanie automatycznie wydrukowane na wyjście, w przeciwnym razie zapisze pustą linię. (dodaj/./!d
to do końcased
skryptu, jeśli jest to niepożądane) .Spowoduje to wykonanie pojedynczego
sed
wywołania dla około 65 000 plików wejściowych - lub dowolnegoARG_MAX
limitu. 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ąF
polecenia, albo możesz wydrukować jefind
na osobnej liście dla każdej partii, dodając-print
główną po+
.źródło
Co powiesz na:
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:
źródło
Wykorzystuje GNU
grep
„s-H
i-n
opcje, 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.
źródło