2009-05-06 1 views
3

У меня есть набор почтовых журналов: mail.log mail.log.0 mail.log.1.gz mail.log.2.gzКак я могу использовать bash (grep/sed/etc), чтобы захватить раздел файла журнала между двумя временными метками?

Каждый из этих файлов содержит хронологически отсортированные строки, начинающиеся со временных меток, таких как:

3 мая 13:21:12 ...

Как я могу легко захватить каждую запись журнала после определенной даты/времени и до другой даты/времени с использованием Баш (и связанных с ними инструментов командной строки) без сравнения каждой отдельной строки? Имейте в виду, что мои даты до и после не могут точно соответствовать вхождениям в лог-файлы.

Мне кажется, что мне нужно определить смещение первой строки больше, чем начальную временную метку, а смещение последней строки меньше, чем конечная временная метка, и как-нибудь вырезать эту секцию.

+0

Когда вы говорите, «с помощью Баш,» вы имеете в виду, что такие инструменты, как Grep не допускаются, или вы имеете в виду более общий Баш сценариев среды, в которой Grep отсчитывает тоже? – JasonSmith

+0

Возможно, вам следует изменить заголовок вопроса, чтобы отразить использование bash? –

+0

Использование grep было бы здорово. Однако файлы очень большие, поэтому сравнение временных меток каждой строки не будет оптимальным. – Brent

ответ

1

Вот одну основную идею о том, как сделать его:

  1. Осмотрите штамп времени на файл, чтобы увидеть, если это irrelevent
  2. Если может быть Релевент, разархивировать в случае необходимости и изучить первые и последние строки файла, чтобы увидеть, если он содержит начало или конец время.
  3. Если это так, используйте рекурсивную функцию , чтобы определить, содержит ли она время начала в первой или второй половине файла. Используя рекурсивную функцию, я думаю, что вы можете найти любую дату в миллионном лог-файле с примерно 20 сравнениями.
  4. не эхо логфайла (ы) в порядке от смещения первой записи к смещению последней записи (больше никаких сравнений)

То, что я не знаю: как лучше читать NTH строка файла (насколько эффективно использовать хвост n + ** n | head 1 **?)

Любая помощь?

+1

хвост (или голова в этом случае) должен подсчитывать символы новой строки, что означает, что он должен будет рассчитывать с любого конца, чтобы найти среднюю линию. И нет стандартной команды UNIX, которая будет бинарно искать файл, как в вашем описании, - вам придется писать свои собственные, и в этом случае вы могли бы также выполнить всю работу в одном оптимизированном исполняемом файле. – paxdiablo

+0

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

0

Возможно, это возможно в среде Bash, но вы должны действительно использовать инструменты, которые имеют более встроенную поддержку для работы со строками и датами. Например, Ruby, похоже, имеет встроенную способность анализировать формат даты. Затем он может преобразовать его в легко сравнимую временную метку Unix (положительное целое число, представляющее секунды с эпохи).

irb> require 'time' 
# => true 

irb> Time.parse("May 3 13:21:12").to_i 
# => 1241371272 

Вы можете легко написать сценарий Ruby:

  • Обеспечить начальную и конечную дату. Преобразуйте их в это временное число Unix.
  • Сканирование файлов журнала по строкам, преобразование даты в ее временную метку Unix и проверка, находится ли она в диапазоне дат начала и окончания.

Примечание: Преобразование в единое целое по времени минимальной метки Unix является хорошим, потому что сравнение целых чисел очень просто и эффективно.

Вы упомянули «без сравнения каждой отдельной строки». Его трудно будет «угадать», где в файле журнала записи становятся слишком старыми или слишком новыми, не проверяя все значения между ними. Однако, если есть действительно монотонно возрастающая тенденция, то вы сразу же знаете, когда прекращать разбор строк, потому что, как только следующая запись будет слишком новой (или старой, в зависимости от расположения данных), вы знаете, что можете остановить поиск. Тем не менее, существует проблема поиска первой строки в желаемом диапазоне.


Я только что заметил ваше редактирование. Вот что я бы сказал:

Если вы действительно беспокоились об эффективном поиске начальной и конечной записи, тогда вы можете выполнить бинарный поиск для каждого. Или, если это кажется излишним или слишком сложным с помощью инструментов bash, вы можете получить эвристику для чтения только 5% строк (по 1 из каждых 20), чтобы быстро приблизиться к точному ответу и затем уточнить, что при желании. Это лишь некоторые предложения по улучшению производительности.

+0

Я могу сделать то же самое с командой date (и проще), но это займет навсегда, если каждая строка должна быть проверена. Одна идея - проверять первую и последнюю строку каждого файла журнала и игнорировать тех, кто находится за пределами диапазона. (например) – Brent

+0

Страница «Дата» говорит «дата - отображение или установка даты и времени». Мне было бы интересно посмотреть, как вы можете прочитать файл и преобразовать «3 мая 13:21:12» в временную метку Unix с помощью команды date. –

+0

date -d "3 мая 13:32:38" +% s – Brent

1

Вы должны посмотреть каждую строку в нужном вам диапазоне (указать, находится ли она в нужном вам диапазоне), поэтому я предполагаю, что вы имеете в виду не каждую строку в файле. На минимальном уровне вам нужно будет посмотреть каждую строку в файле вплоть до первого за пределами вашего диапазона (я предполагаю, что строки указаны в порядке даты/времени).

Это довольно простая схема:

state = preprint 
for every line in file: 
    if line.date >= startdate: 
     state = print 
    if line.date > enddate: 
     exit for loop 
    if state == print: 
     print line 

Вы можете написать в AWK, Perl, Python, даже COBOL, если необходимо, но логика всегда одинакова.

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

Если это что-то, что вы собираетесь делать довольно часто, вы можете подумать о переносе усилий с «каждый раз, когда вы это делаете» на «один раз, когда файл стабилизируется». Примером может быть загрузка строк журнала в базу данных, индексированная по дате/времени.

Это займет некоторое время, чтобы настроить, но в результате ваши запросы станут намного быстрее. Я не обязательно защищаю базу данных - вы, вероятно, может достичь того же эффекта путем разделения файлов журнала в ежечасно регистрирует таким образом:

2009/ 
    01/ 
    01/ 
     0000.log 
     0100.log 
     : : 
     2300.log 
    02/ 
    : : 

Затем в течение определенного времени, вы точно знаете, с чего начать и прекратить смотреть. Диапазон 2009/01/01-15:22 через 2009/01/05-09:07 привело бы:

  • некоторые (последний бит) файла 2009/01/01/1500.txt.
  • все файлы 2009/01/01/1[6-9]*.txt.
  • все файлы 2009/01/01/2*.txt.
  • все файлы 2009/01/0[2-4]/*.txt.
  • все файлы 2009/01/05/0[0-8]*.txt.
  • некоторые (первые бит) файла 2009/01/05/0900.txt.

Конечно, я бы написал сценарий, чтобы возвращать эти строки, а не пытаться делать это вручную каждый раз.

+0

Нет, я уверен, что вам не нужно смотреть на каждую строку. Как насчет того, чтобы сначала просмотреть среднюю запись файла и отклонить половину файла сразу с места в карьер? – Brent

+0

На самом деле, я уверен, что вы это сделали, так как вы хотели использовать стандартные инструменты UNIX - я не знаю никого, кто делает то, что вы описываете (т. Е. Fseek в середине и находите ближайшую строку). Вы можете написать один, но, если вы это делаете, открываются все новые возможности. – paxdiablo

+0

Я уверен, что условие выхода (line.date> enddate) никогда не будет выполнено, потому что a) это предложение else (если line.date> = startdate) и b) enddate> = startdate.исправить, изменив порядок этих двух операторов if: if linedate> end then exit loop else if linedate> start then print line – cas

5

Преобразование мин/макс даты в «секундах с начала эпохи»,

MIN=`date --date="$1" +%s` 
MAX=`date --date="$2" +%s` 

Преобразовать первые n слова в каждой строке журнала к тому же,

L_DATE=`echo $LINE | awk '{print $1 $2 ... $n}'` 
L_DATE=`date --date="$L_DATE" +%s` 

Сравнить и выбрасывайте линии до вы достигаете MIN,

if (($MIN > $L_DATE)) ; then continue ; fi 

Сравнить и распечатать lin эс, пока вы не достигнете MAX,

if (($L_DATE <= $MAX)) ; then echo $LINE ; fi 

Выход при превышении MAX.

if (($L_DATE > $MAX)) ; then exit 0 ; fi 

Весь сценарий minmaxlog.ш выглядит так,

#!/usr/bin/env bash 

MIN=`date --date="$1" +%s` 
MAX=`date --date="$2" +%s` 

while true ; do 
    read LINE 
    if [ "$LINE" = "" ] ; then break ; fi 

    L_DATE=`echo $LINE | awk '{print $1 " " $2 " " $3 " " $4}'` 
    L_DATE=`date --date="$L_DATE" +%s` 

    if (($MIN > $L_DATE )) ; then continue ; fi 
    if (($L_DATE <= $MAX)) ; then echo $LINE ; fi 
    if (($L_DATE > $MAX)) ; then break ; fi 
done 

Я побежал на этот файл minmaxlog.input,

May 5 12:23:45 2009 first line 
May 6 12:23:45 2009 second line 
May 7 12:23:45 2009 third line 
May 9 12:23:45 2009 fourth line 
June 1 12:23:45 2009 fifth line 
June 3 12:23:45 2009 sixth line 

как это,

./minmaxlog.sh "May 6" "May 8" < minmaxlog.input 
+0

Я действительно не думаю, что bash - это способ пойти на эффективность. Это очень просто, да, но если вы хотите быстро рассмотреть возможность использования C с помощью lseek() и двоичного поиска. – Dylan

+0

Зачем ухаживать за работой, если вы собираетесь делать это раз или два? – Daniel

+0

@ Даниэль, где было сказано, что это будет сделано только один или два раза? – paxdiablo

1

Может быть, вы можете попробовать это:

sed -n "/BEGIN_DATE/,/END_DATE/p" logfile 
+1

Нет, даты начала и окончания могут не соответствовать любым элементам. –

Смежные вопросы