Использование данных вашего примера:
$ join <(sort file1) <(sort file2)
ARS-BFGL-BAC-10975 0.9303 688423261 1 01/04/2015 0.9983763305 10 21225382
ARS-BFGL-BAC-10975 0.9303 688423263 1 01/04/2015 0.9983763305 10 21225382
ARS-BFGL-BAC-11025 0.9092 688423261 1 01/04/2015 0.9983763305 10 84516867
ARS-BFGL-BAC-11025 0.9092 688423263 1 01/04/2015 0.9983763305 10 84516867
ARS-BFGL-BAC-11193 0.9544 688423261 1 01/04/2015 0.9983763305 1 29303546
ARS-BFGL-BAC-11193 0.9544 688423263 1 01/04/2015 0.9983763305 1 29303546
Похоже, это делает то, что вы от него хотите, за исключением порядка строк в выводе (, который отсортирован здесь, и я не знаю, важно ли это для вас. или нет ).
Поле соединения по умолчанию является первым полем, поэтому -j 1
не требуется (это расширение GNU join
).
Выход по умолчаниюjoin
:«Каждый строка вывода состоит из поля соединения, остальные поля из файла1 а затем оставшиеся поля из файла2" (из руководства OpenBSD ). Это означает, что спецификация поля вывода, которую вы использовали, также не нужна, поскольку она соответствует поведению по умолчанию.
Для sort
-k1
также эквивалентно значению по умолчанию.
Я не знаю, что вызвало сообщение об ошибке, кроме случайного использования sort -c
.
С помощью ts
из moreutils
вы можете легко преобразовать эти метки времени в более удобный формат:
ts -r %FT%T%z < file.log |
awk '$0 > "[2019-12-04T02:50" && tolower($0) ~ /failed login/'
Что на вашем вводе (и в часовом поясе America/Los_Angeles
)дает:
[2019-12-04T03:05:18-0800] access WARNING 10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser
ts
с -r
анализирует эту метку времени и преобразует ее в указанный формат%FT%T%z
strftime
(в вашем часовом поясе ).
Поскольку этот формат YYYY-MM-DDTHH:MM:SS
сортирует одинаково лексически и в хронологическом порядке¹, тогда просто нужно выполнить сравнение строк в awk
, чтобы найти эти записи позже указанной даты. awk
также может выполнять работу grep -i
. Здесь используется стандартный подход tolower()
для совпадения без учета регистра. С GNU awk
вы также можете:
gawk -v IGNORECASE=1 '$0 > "[2019-12-04T02:50" && /failed login/'
Если у вас нет moreutils
, вы можете выполнить синтаксический анализ с помощью perl
, Time::Piece
, например,(ts
— это perl-скрипт, который использует Date::Parse
, но в отличие от Time::Piece
, это не так. один из основных модулей perl
, поэтому может не устанавливаться в вашей системе):
CUT=2019-12-04T02:50:00-0800 perl -MTime::Piece -F'[][]' -ale '
BEGIN{$cut = Time::Piece->strptime($ENV{CUT}, "%FT%T%z")}
print if /failed login/i &&
Time::Piece->strptime($F[1], "%d/%b/%Y %T %z") >= $cut' < file.log
¹ если мы проигнорируем метки во время смены зимних/летних часов в часовых поясах, которые переходят на летнее время
Я оставляю это на случай, если кому-то это покажется полезным, но вместо этого просто используйте этот ответ . Это намного проще и эффективнее.
Вот перловый способ:
$ perl -lne 'if(/^\[([^]]+)/){$d=$1; chomp($dateThreshold=`date -d "04 Dec 2019" +%s`); $d=~s|/| |g; chomp($d=`date -d "$d" +%s`); print if $d >= $dateThreshold;} ' test.log
[04/Dec/2019 02:05:13 -0800] access WARNING 10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser
[04/Dec/2019 02:05:15 -0800] access WARNING 10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser
[04/Dec/2019 02:04:59 -0800] access INFO 10.126.49.92 ahmed.rao - "POST /notebook/api/check_status HTTP/1.1" returned in 759ms
[04/Dec/2019 02:05:00 -0800] base INFO Selected cluster 0e83a448-26c9-459b-a0f2-3478ecb119af {u'interface': u'impala', u'namespace': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'type': u'direct', u'id': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'name': u'0e83a448-26c9-459b-a0f2-3478ecb119af'} interface hiveserver2
[04/Dec/2019 03:05:00 -0800] access INFO 10.126.49.92 ahmed.rao - "POST /notebook/api/close_statement HTTP/1.1" returned in 1345ms
[04/Dec/2019 03:05:00 -0800] base INFO Selected cluster 0e83a448-26c9-459b-a0f2-3478ecb119af {u'interface': u'impala', u'namespace': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'type': u'direct', u'id': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'name': u'0e83a448-26c9-459b-a0f2-3478ecb119af'} interface hiveserver2
[04/Dec/2019 03:05:18 -0800] access WARNING 10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser
И, немного яснее:
perl -lne 'if(/^\[([^]]+)/){ ## skip lines that do not match
## Save the date of the current line as $d
$d=$1;
## Replace all slashes with pipes so the 'date' command
## can read this as a date.
$d=~s|/| |g;
## Now, translate $d into seconds since the epoch
chomp($d=`date -d "$d" +%s`);
## Set the threshold date in seconds since the epoch.
chomp($dateThreshold=`date -d "04 Dec 2019" +%s`);
## Print this line if its date is greater than or equal to the threshold
print if $d >= $dateThreshold;
} ' test.log
Наконец, вы можете сделать его немного более эффективным, переместив шаг, устанавливающий порог, в блок BEGIN
, чтобы он выполнялся только один раз при запуске скрипта:
perl -lne 'BEGIN{chomp($dateThreshold=`date -d "04 Dec 2019" +%s`); } if(/^\[([^]]+)/){$d=$1; $d=~s|/| |g; chomp($d=`date -d "$d" +%s`); print if $d >= $dateThreshold;} ' test.log
с помощью sed:
sed -n "/$(date +'%d\/%b\/%Y')/,/*/p" test.log | grep -i "failed login"
ПРИМЕЧАНИЕ. :Текущая дата должна быть доступна в файле журнала.
Вот еще один ответ, использующий GNU awk
, который прибегает к вызову команды GNU date
.
Программа awk
(вызовемfind_after_timestamp.awk
)вот так:
BEGIN{
gsub("/"," ",start_datetime)
extcmd=sprintf("date -d \"%s\" +\"%%Y %%m %%d %%H %%M %%S\"",start_datetime)
extcmd | getline startstring
close(extcmd)
start_ts=mktime(startstring)
print "Lines will be matched starting with timestamp",start_ts
printf("Will look for: \"%s\"\n",searchpat)
}
{
if (match($0,/^\[([[:print:]]*)\][[:print:]]*$/,line_datetime)==0) next
gsub("/"," ",line_datetime[1])
extcmd=sprintf("date -d \"%s\" +\"%%Y %%m %%d %%H %%M %%S\"",line_datetime[1])
extcmd | getline line_dtstring
close(extcmd)
line_ts=mktime(line_dtstring)
if (line_ts > start_ts && $0 ~ searchpat) print
}
Вы бы назвали это как
awk -v start_datetime="04/Dec/2019 02:05:21 -0800" -v searchpat="[Ff]ailed login" -f find_after_timestamp.awk test.log
Где переменная start_datetime
будет началом вашего диапазона поиска, т. е. будут учитываться все записи с датой/временем, равным этому моменту времени или после него. Значение start_datetime
должно иметь тот же формат, что и в вашем лог-файле, но, кроме того, является произвольным и не обязательно должно быть значением, фактически присутствующим в файле. Переменная searchpat
будет содержать искомый шаблон.
Эта конструкция вращается вокруг преобразования вашей (скорее «не -стандартной» )спецификации даты/времени DD/MONTH/YYYY HH:MM:SS TIMEZONE
во что-то, понятное GNU date
, путем замены /
в части даты на пробелы с использованием gsub
.
Затем он вызывает внешнюю команду date
, выполняя строку extcmd
в оболочке и считывая результат в строковую переменную(startstring
на этапе установки, line_dtstring
на этапе разбора файла -), который теперь отформатирован так, чтобы встроенная функция awk
mktime
могла анализировать ist.
Команда mktime
преобразует удобочитаемую -дату/время в числовое -UNIX-время, которое можно сравнивать с помощью арифметического сравнения.
В фазе BEGIN
это делается для преобразования спецификации даты начала, в основной части это делается для преобразования временной метки, связанной с текущей строкой. Строки без метки времени будут игнорироваться(if (match(...)==0) next
).
Если отметка времени текущей строки больше (= позже ), чем опорная отметка времени начала, И в строке найдено searchpat
, строка будет напечатана.
Я знаю, что обращение к внешним программам в awk
программе вызывает неодобрение, но это поможет с базовыми инструментами, доступными практически при любой установке.