Знайдіть останнє виникнення рядка в декількох файлах


9

Мені потрібно шукати декілька файлів журналів (усі файли, створені за останні 24 години, всі зберігаються в одному каталозі), щоб знайти останнє виникнення рядка. Це команда, яку я написав:

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

Але це повертає лише останній рядок для одного файлу. Будь-які пропозиції, як підключити це, щоб отримати всі рядки?


ви намагалися перевернути хвіст і останній греп? знайти. -мм 1 | grep fileprefix | хрящ хвіст -1 | grep 'рядок пошуку'
Матьє

Відповіді:


4

Якщо передбачити засоби GNU:

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

Чи можете ви, будь ласка, розробити мету 'bash -c \', оскільки я вже використовую bash shell. Також призначення '_ {} +' наприкінці.
Локеш

@Lokesh, ви можете приступити findдо виконання команд у файлах за допомогою -exec. З bash -c, ми породжуючи bashоболонку, петлі через файли знайдені findі виконує tac .. | grep -m1 fileprefixна кожному
Iruvar

Я намагався розширити фільтрацію рядків для циклу, включивши команду cut, тобто для f; do tac "$ f" | grep -m1 fileprefix | cut -d '' -f4,7-8, але в момент, коли я ставлю команду cut, це дає мені помилку несподіваного закінчення файлу. Чи можете ви підказати, що я роблю не так.
Локеш

@lokesh, використовувати -d" "з вирізом. Подвійні цитати замість одинарних
iruvar

1
findКоманда може фільтрувати для префікса файлу; для цього grepне потрібно. Також дивно, що рядок пошуку не відповідає цій відповіді.
Джонатан Леффлер

8

Якщо все знаходиться в одному каталозі, ви можете зробити:

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

Якщо це великі файли, можливо, варто прискорити роботу, використовуючи tacдля друку файл у зворотному порядку (спочатку останній рядок), а потім, grep -m1щоб відповідати першому виникненню. Таким чином, вам не доведеться читати весь файл:

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

Обидва з них припускають, що каталогів немає fileprefix. Якщо є, ви отримаєте помилку, яку можете просто проігнорувати. Якщо це проблема, перевірте лише файли:

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

Якщо вам також потрібно надрукувати ім’я файлу, додайте -Hдо кожного grepвиклику. Або, якщо ваш grepне підтримує, скажіть, щоб він також шукав /dev/null. Це не змінить вихід, але оскільки grepйому надано кілька файлів, він завжди буде друкувати ім'я файлу для кожного звернення:

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

"Таким чином, вам не доведеться читати весь файл" - е-е? Ні, ви уникаєте читати весь файл у форматі "grep", але замість цього перекладаєте весь файл через tac. Мені не зрозуміло, що це буде швидше, хоча це залежатиме від того, відповідність була біля початку чи кінця файлу.
Жил "ТАК - перестань бути злим"

@Gilles ні, ти не передаєш весь файл через tacбудь-який. Він закриється, як тільки буде знайдено перший матч. Я щойно тестував текстовий файл 832M та візерунок, знайдений в останньому рядку. grep -m 1 pattern fileінструмент ~ 7 секунд і tac file | grep -m1 patternзайняв 0.009.
terdon

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

... працюватиме, якщо у вас є GNU, sedякий підтримує -sпараметри файлів і POSIX find.

Вам, мабуть, слід додати ! -type dабо -type fкласифікатори, оскільки намагання прочитати каталог не дуже корисне, а подальше звуження діапазону до звичайних файлів може уникнути зависання читання на трубі або файлі послідовного пристрою.

Логіка неймовірно проста - sedперезаписує свій hстарий пробіл копією будь-якого вхідного рядка, який відповідає searchstring, потім dвибирає з виводу всі рядки введення, але останні для кожного вхідного файлу. Коли він дістається до останнього рядка, він xзмінює пробіли утримування та шаблону, і, якщо searchstringйого взагалі знайдено, коли він читає файл, остання така поява буде автоматично віддрукована для виводу, інакше пише порожній рядок. (додайте /./!dдо хвоста sedсценарію, якщо це небажано) .

Це зробить одне sedвиклик на деякі 65k вхідні файли - або будь-який ваш ARG_MAXліміт. Це повинно бути дуже ефективним рішенням і реалізується досить просто.

Якщо ви також хочете, щоб імена файлів, з огляду на останній GNU, sedви можете виписати їх в окремі рядки з Fкомандою, інакше ви можете їх надрукувати findв окремому списку за партією, додавши -printосновний після +.


1

Як щодо:

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

Вищенаведене дає хороший вихід із останнім виникненням рядка пошуку у кожному файлі з наступним іменем файлу після коми (змініть частину ", $ 1" під відлунням, щоб змінити форматування або видалити його, якщо це не потрібно). Вибірка вибору, що шукає рядок пошуку "10" у файлах із префіксом імені "файл", виглядає наступним чином:

[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] }}'

Це використовує GNU grep«s -Hі -nопції завжди друкувати як ім'я файлу і LINENUMBER всіх матчів, то він сортує по імені файлу і LINENUMBER, і труби її в AWK, який зберігає останній матч для кожного файлу в масив, і в кінці кінців друк це.

Досить жорстокий метод, але він працює.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.