Греп от конца файла до начала

У меня есть файл с около 30.000.000 строк (Radius Accounting), и мне нужно найти последнее совпадение данного шаблона.

Команда:

tac accounting.log | grep $pattern 

дает то, что мне нужно, но это слишком медленно, потому что ОС должна сначала прочитать весь файл, а затем отправить в трубу.

Итак, мне нужно что-то быстрое, чтобы прочитать файл с последней строки до первой.

4 Solutions collect form web for “Греп от конца файла до начала”

tac помогает, если вы также используете grep -m 1 (при условии, что GNU grep ) имеет grep остановку после первого совпадения:

 tac accounting.log | grep -m 1 foo 

От man grep :

  -m NUM, --max-count=NUM Stop reading a file after NUM matching lines. 

В примере в вашем вопросе, как tac и grep необходимо обработать весь файл, поэтому использование tac является беспредметным.

Итак, если вы не используете grep -m , не используйте tac вообще, просто проанализируйте вывод grep чтобы получить последнее совпадение:

 grep foo accounting.log | tail -n 1 

Другой подход – использовать Perl или любой другой язык сценариев. Например (где $pattern=foo ):

 perl -ne '$l=$_ if /foo/; END{print $l}' file 

или

 awk '/foo/{k=$0}END{print k}' file 

Причина почему

 tac file | grep foo | head -n 1 

не останавливается в первом матче из-за буферизации.

Как правило, head -n 1 выходит после прочтения строки. Таким образом, grep должен получить SIGPIPE и выйти, как только он напишет вторую строку.

Но происходит то, что, поскольку его вывод не идет на терминал, grep его буферизирует. То есть, он не записывает его, пока он не накопился достаточно (4096 байт в моем тесте с GNU grep).

Это означает, что grep не выйдет, прежде чем он написал 8192 байта данных, поэтому, вероятно, довольно много строк.

С помощью GNU grep вы можете --line-buffered его, используя --line-buffered которая сообщает ему, что строки будут записываться, как только они будут найдены, независимо от того, идет ли он на терминал или нет. Таким образом, grep выйдет на вторую найденную строку.

Но с GNU grep любом случае вы можете использовать -m 1 вместо этого, как показал @terdon, что лучше, поскольку он выходит в первом матче.

Если ваш grep не является GNU grep , вы можете использовать sed или awk . Но tac является командой GNU, я сомневаюсь, что вы найдете систему с tac где grep не является GNU grep .

 tac file | sed "/$pattern/!d;q" # BRE tac file | P=$pattern awk '$0 ~ ENVIRON["P"] {print; exit}' # ERE 

Некоторые системы имеют tail -r чтобы делать то же самое, что и GNU tac .

Обратите внимание, что для обычных файлов с возможностью поиска tac и tail -r эффективны, потому что они читают файлы в обратном порядке, они не просто полностью читают файл в памяти перед его печатью назад (как подход sed @ slm или tac на non -регулярные файлы).

В системах, где не доступны ни tac ни tail -r , единственными опциями являются реализация обратного чтения вручную с такими языками программирования, как perl или использование:

 grep -e "$pattern" file | tail -n1 

Или:

 sed "/$pattern/h;$!d;g" file 

Но это означает найти все матчи и только напечатать последний.

Вот возможное решение, которое найдет местоположение первого появления шаблона последним:

 tac -s "$pattern" -r accounting.log | head -n 1 

Это использует -s и -r переключатели tac которые заключаются в следующем:

 -s, --separator=STRING use STRING as the separator instead of newline -r, --regex interpret the separator as a regular expression 

Использование sed

Показывая некоторые альтернативные методы для прекрасного ответа @ Terdon с помощью sed :

 $ sed '1!G;h;$!d' file | grep -m 1 $pattern $ sed -n '1!G;h;$p' file | grep -m 1 $pattern 

Примеры

 $ seq 10 > file $ sed '1!G;h;$!d' file | grep -m 1 5 5 $ sed -n '1!G;h;$p' file | grep -m 1 5 5 

Использование Perl

В качестве бонуса здесь немного проще обозначить Perl:

 $ perl -e 'print reverse <>' file | grep -m 1 $pattern 

пример

 $ perl -e 'print reverse <>' file | grep -m 1 5 5 
  • Выполнение хвоста -f в текстовом файле поверх sshfs не работает. Зачем?
  • Файл журнала хвоста на нескольких машинах по ssh
  • Как я могу напечатать вторую в последнюю строку множество файлов в один файл?
  • Tail Grep - печать окружающих линий до тех пор, пока шаблон не будет сопоставлен
  • Можно ли изменить количество строк по умолчанию хвоста?
  • Перенаправление вывода GREP в разные текстовые файлы в зависимости от содержимого захвата
  • Проводя результат ls в хвост
  • grep не работает в цикле for по списку
  • Будет ли «хвост -100»? sed -n 1p "читать только одну строку?
  • Как изменить цвет символа при хвосте и tr
  • Команда для удаления первого N числа строк на входе
  • Linux и Unix - лучшая ОС в мире.