2016-01-22 3 views
2

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

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

text... 2016-01-22 10:03:41 more text... 

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

Прямо сейчас, похоже, gawk's mktime() Функция является ключом к этому. К сожалению, это требует ввода в следующем формате:

yyyy mm dd hh mm ss 

Я в настоящее время поиски тестовый файл (называемый timecomp) для регулярного выражения, соответствующего формата даты.

Редактировать: тестовый файл содержит только дату и время на каждой строке, никакого другого текста.

Я использовал sed для замены разделителей (т.е. /, -, и :) с пространством, а затем поступает на выход в поглазеть сценарий под названием stime с помощью следующего оператора:

sed -e 's/[-://_]/ /g' timecomp | gawk -f stime 

Вот сценарий

# stime 
BEGIN { tsec=systime(); } /.*20[1-9][0-9] [0-1][1-9] [0-3][0-9] [0-2][0-9][0-6][0-9] [0-6][0-9]/ { 
    if (tsec < mktime($0)) 
     print "\t" $0 # the tab is just to differentiate the desired output from the other lines that are being printed. 
} $1 

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

2016 01 22 13 23 20 
2016 01 22 14 56 57 
2016 01 22 15 46 46 
2016 01 22 16 32 30 
    2016 01 22 18 56 23 
2016 01 22 18 56 23 
    2016 01 22 22 22 28 
2016 01 22 22 22 28 
    2016 01 22 23 41 06 
2016 01 22 23 41 06 
    2016 01 22 20 32 33 

Как напечатать только строки в будущем?

Примечание: Я делаю это на Mac, но я хочу, чтобы он был переносимым для Linux, потому что я в конечном итоге делаю это для выполнения некоторых задач, которые я должен выполнять на работе.

Я хотел бы попытаться выполнить это в одном скрипте, а не требовать, чтобы заявление sed изменило даты, но я столкнулся с другими проблемами, которые, вероятно, требуют другого вопроса, поэтому я придерживаюсь этого на данный момент ,

Любая помощь была бы принята с благодарностью! Благодаря!


Ответил: У меня был $1 в последней строке моего сценария, и это было причиной дополнительного выхода.

+0

Что такое $ 1 в конце? –

+0

Ваш файл не содержит * any * of ':: _ _ вне даты? Потому что на данный момент вы заменяете их глобально пробелами. Кроме того, почему вы заменяете '/' и '_'? Они не являются частью строки даты. –

+0

'$ 0' по-прежнему является полной записью, включая' text' и 'more text'; разве вы не хотите, чтобы 'mktime' получал только дату/время в качестве аргументов? –

ответ

1

Вместо AWK, это (почти) чистый Bash решение:

#!/bin/bash 

# Regex for time string 
re='[0-9]{4}-[0-9]{2}-[0-9]{2} ([0-9]{2}:){2}[0-9]{2}' 

# Current time, in seconds since epoch 
now=$(date +%s) 

while IFS= read -r line; do 

    # Match time string 
    [[ $line =~ $re ]] 
    time_string="${BASH_REMATCH[0]}" 

    # Convert time string to seconds since epoch 
    time_secs=$(date -d "$time_string" +%s) 

    # If time is in the future, print line 
    if ((time_secs > now)); then 
     echo "$line" 
    fi 

done < <(grep 'pattern' "$1") 

Это имеет преимущество из Coreutils date форматирования для преобразования даты в секундах с начала эпохи для удобного сравнения двух дат:

$ date 
Fri, Jan 22, 2016 11:23:59 PM 
$ date +%s 
1453523046 

И -d аргумент взять строку в качестве входных данных:

$ date -d '2016-01-22 10:03:41' +%s 
1453475021 

Скрипт выполняет следующие действия:

  • Фильтр входной файл с Grep (для строк, содержащих общий pattern, но может быть что угодно)
  • Loop над строками, содержащими pattern
  • Match линию с регулярным выражением что соответствует дате/время строки yyyy-mm-dd hh:mm:ss и извлечь матч
  • преобразования времени строки в секунды с эпохи
  • Сравните это значение времени в $now, который текущая дата/время в секундах, начиная с эпохой
  • Если время от логфайла в будущем, печать линии

Для примера входного файла как этого

text 2016-01-22 10:03:41 with time in the past 
more text 2016-01-22 10:03:41 matching pattern but in the past 
other text 2017-01-22 10:03:41 in the future matching pattern 
some text 2017-01-23 10:03:41 in the future but not matching 
blahblah 2022-02-22 22:22:22 pattern and also in the future 

Результата is

$ date 
Fri, Jan 22, 2016 11:36:54 PM 
$ ./future_time logfile 
other text 2017-01-22 10:03:41 in the future matching pattern 
blahblah 2022-02-22 22:22:22 pattern and also in the future 
+0

Отлично! Я играю с реализацией bash, и я получил его для работы с ограниченным успехом, но, к сожалению, есть несколько файлов, которые я намерен использовать для этого, и у многих из них есть совершенно разные форматы даты и времени , Я пробовал разные подходы с bash, но в итоге столкнулся с стеной, потому что я совершенно не знал о BASH_REMATCH и операторе сравнения регулярных выражений, пока вы не разместили это. (Я написал свою первую баш-функцию около двух или трех месяцев назад, поэтому мне нужно многому научиться!). Спасибо! Я получил версию awk, но это все равно будет полезно! – Boncrete

+0

Извините, я толкнул ключ возврата! Ха-ха еще раз спасибо! – Boncrete

+0

@Boncrete 'date -d' очень умен в строке даты - почти любой формат разбирается правильно! Но, конечно же, необходимо будет обновить регулярное выражение Bash. –

1

Это то, что я сейчас работаю. Он работает для нескольких разных форматов даты и фактических файлов, которые имеют не только дату и время. Формат по умолчанию, в котором он работает, равен yyyy/mm/dd, но при необходимости он принимает аргумент, чтобы указать формат mm/dd/yyyy.

BEGIN { tsec=systime(); dtstr=""; dt[1]="" } /.*[0-9][0-9]:[0-9][0-9]:[0-9][0-9]/ { 
cur=$0 

if (fm=="mdy") { 
    match($0,/[0-1][1-9][-_\/][0-3][0-9][-_\/]20[1-9][0-9]/)  # mm dd yyyy 
    section=substr($0,RSTART,RLENGTH) 
    split(section, dt, "[-_//]") 
    dtstr=dt[3] " " dt[1] " " dt[2] 
    gsub(/[0-1][1-9][-\/][0-3][0-9][-\/]20[1-9][0-9]/, dtstr, cur) 
} 

gsub(/[-_:/,]/, " ", cur) 
match(cur,/20[1-9][0-9] [0-1][1-9] [0-3][0-9][[:space:] ]*[0-2][0-9] [0-6][0-9] [0-6][0-9]/) 
arr=mktime(substr(cur,RSTART,RLENGTH)) 

if (tsec < arr) 
    print $0 
} 

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

gawk -f stime fm=mdy filename 

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

+0

Решение awk будет намного быстрее, чем, кстати, мой Баш. Баш медленный. –

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