Bash скрипт для извлечения записей из файла журнала на основе дат, указанных в другом файле? - PullRequest
2 голосов
/ 03 сентября 2010

У меня есть довольно большой файл журнала CSV с разделителями-запятыми (> 50000 строк, назовем его file1.csv), который выглядит примерно так:

field1,field2,MM-DD-YY HH:MM:SS,field4,field5...
...
field1,field2,07-29-10 08:04:22.7,field4,field5...
field1,field2,07-29-10 08:04:24.7,field4,field5...
field1,field2,07-29-10 08:04:26.7,field4,field5...
field1,field2,07-29-10 08:04:28.7,field4,field5...
field1,field2,07-29-10 08:04:30.7,field4,field5...
...

Как видите, естьполе посередине, которое является отметкой времени.

У меня также есть файл (назовем его file2.csv), который имеет короткий список раз:

timestamp,YYYY,MM,DD,HH,MM,SS
20100729180031,2010,07,29,18,00,31
20100729180039,2010,07,29,18,00,39
20100729180048,2010,07,29,18,00,48
20100729180056,2010,07,29,18,00,56
20100729180106,2010,07,29,18,01,06
20100729180115,2010,07,29,18,01,15

Что бы я хотелкак сделать, это извлечь только те строки в file1.csv, которые имеют время, указанное в file2.csv.

Как мне сделать это с помощью скрипта bash?Поскольку file1.csv довольно большой, эффективность также может быть проблемой.Раньше я делал очень простые сценарии bash, но на самом деле не знаю, как с этим справиться.Возможно, какая-то реализация awk?Или есть другой способ?

PS Осложнение 1: я вручную проверил некоторые записи в обоих файлах, чтобы убедиться, что они совпадают, и они соответствуют.Нужно просто найти способ удалить (или проигнорировать) лишнее поле «.7» в конце секунд («SS») в file1.csv.

PPS Complication 2: исключает записив list1.csv все разделены примерно двумя секундами.Иногда метки времени в list2.csv попадают прямо между двумя записями в list1.csv!Есть ли способ найти наиболее близкое совпадение в этом случае?

Ответы [ 3 ]

2 голосов
/ 03 сентября 2010

Воспользовавшись ответом Джона, вы можете сортировать и объединять файлы, печатая только нужные вам столбцы (или все столбцы, если это так). Пожалуйста, посмотрите ниже (обратите внимание, что я полагаю, что вы используете UNIX, например Solaris, поэтому nawk может быть быстрее, чем awk, также у нас нет gawk, который мог бы облегчить еще больше):

# John's nice code
awk -F, '! /timestamp/ {print $3 "-" $4 "-" ($2-2000) " " $5 ":" $6 ":" $7}' file2.csv > times.list
# Sorting times.list file to prepare for the join
sort times.list -o times.list
# Sorting file1.csv
sort -t, -k3,3 file1.csv -o file1.csv
# Finally joining files and printing the rows that match the times
join -t, -1 3 -2 1 -o 1.1 1.2 1.3 1.4 1.5......1.50 file1.csv times.list 

Одна особенность этого метода в том, что вы можете изменить его, чтобы он работал в нескольких разных случаях, например, с разным порядком столбцов, а также в тех случаях, когда ключевые столбцы не объединяются. Было бы очень трудно сделать это с помощью grep (используя регулярное выражение или нет)

1 голос
/ 03 сентября 2010

Если у вас есть GNU awk (gawk), вы можете использовать эту технику.

Чтобы соответствовать ближайшим временам, одним из подходов было бы сделать так, чтобы awk печатал две строки для каждой строки в file2.csv,затем используйте это с grep -f, как в ответе Джона Кугельмана .Во второй строке будет добавлена ​​одна секунда.

awk -F, 'NR>1 {$1=""; print strftime("%m-%d-%y %H:%M:%S", mktime($0));
                               print strftime("%m-%d-%y %H:%M:%S", mktime($0) + 1)}' file2.csv > times.list
grep -f times.list file1.csv

Это иллюстрирует несколько различных техник.

  • пропустить запись номер один, чтобы пропустить заголовок (использование соответствияна самом деле лучше)
  • вместо того, чтобы иметь дело с каждым полем отдельно, $1 очищается и strftime создает вывод в нужном формате
  • mktime преобразует строку в формат "гггг"mm dd hh mm ss "(-F, и присвоение $1 удаляет запятые) на количество секунд с начала эпохи, и мы добавляем 1 к нему для второй строки
1 голос
/ 03 сентября 2010

Один из подходов заключается в использовании awk для преобразования меток времени в file2.csv в формат file1.csv, а затем grep -f для поиска в file1.csv.Это должно быть довольно быстро, так как он сделает только один проход через file1.csv.

awk -F, '! /timestamp/ {print $3 "-" $4 "-" ($2-2000) " " $5 ":" $6 ":" $7}' file2.csv > times.list
grep -f times.list file1.csv

Вы можете объединить все это в одну строку, если хотите:

grep -f <(awk -F, '! /timestamp/ {print $3 "-" $4 "-" ($2-2000) " " $5 ":" $6 ":" $7}' file2.csv) file1.csv
...