Bash在时间戳之前找到最后一个条目

时间:2013-08-23 13:50:57

标签: bash grep

我有一个格式化的.csv文件;

myfile.csv

**Date,Timestamp,Data1,Data2,Data3,Data4,Data5,Data6**  
20130730,22:08:51.244,APPLES,Spain,67p,blah,blah  
20130730,22:08:51.244,PEARS,Spain,32p,blah,blah  
20130730,22:08:51.708,APPLES,France,102p,blah,blah  
20130730,22:10:62.108,APPLES,Spain,67p,blah,blah  
20130730,22:10:68.244,APPLES,Spain,67p,blah,blah  

我希望输入一个时间戳,该时间戳很可能与文件中的那些时间段完全匹配毫秒,并找到与特定grep搜索匹配的前一行。

所以,例如类似的东西;

cat myfile.csv | grep 'Spain' | grep 'APPLES' | grep -B1 "22:09"

应该返回

20130730,22:08:51.244,APPLES,Spain,67p,blah,blah

但到目前为止,我只能在grep中使用精确的时间戳。有没有办法让它把这些作为时间序列来对待? (我猜这就是问题所在 - 它正在尝试纯粹的模式匹配,而不是无理地找不到它)

3 个答案:

答案 0 :(得分:2)

你可能有一个awk,它在内存中保留了它看到的最后一行,它的时间戳低于你提供的时间戳,并在最后打印最后一个匹配(考虑到它们按升序排列)

例如:

awk  -v FS=',' -v thetime="22:09" '($2 < thetime) { before=$0 ; }  END { print before ; }' myfile.csv

这种情况恰好在您输入一个字符串时可以正常工作,从字面上看,它不需要具有完整的大小(即22:09:00.000)进行比较。

同样,但为了便于阅读,有几行:

awk  -v FS=',' -v thetime="22:09" '
   ($2 < thetime) { before=$0 ; }  
   END            { print before ; }' myfile.csv

现在,如果我了解您的完整要求:您需要在一个国家和一个产品类型的行中找到时间戳之前的最后一行吗?然后:

awk -v FS=',' -v thetime="${timestamp}" -v country="${thecountry}" -v product="${theproduct}" '
   ( $4 == country ) && ( $3 == product ) && ( $2 < thetime ) { before=$0 ; }
   END             { print before ; }'  myfile.csv

应该适合你...(用10:07,西班牙和APPLES喂它,它返回预期的“20130730,22:08:51.244,APPLES,西班牙,67p,等等,等等”)

如果您的文件有几天(为了解决Bentoy13的问题),

awk -v FS=',' -v theday="${theday}" -v thetime="${timestamp}" -v thecountry="${thecountry}" -v theproduct="${theproduct}" '
   ( $4 == thecountry ) && ( $3 == theproduct ) && (($1<theday)||(($1==theday)&&($2<thetime))) { before=$0 ; }
   END             { print before ; }'  myfile.csv

如果第一列更改(即,如果它跨越几天),那么最后一个也可以工作,但是你还需要在当天提供它

答案 1 :(得分:2)

我还有一个使用awk的精彩解决方案:

awk -F ',' -v mytime="2013 07 30 22 09 00" '
  BEGIN {tlimit=mktime(mytime); lastline=""}
  {
    l_y=substr($1,0,4); l_m=substr($1,4,2); l_d=substr($1,6,2);
    split($2,l_hms,":"); l_hms[3]=int(l_hms[3]);
    line_time=mktime(sprintf("%d %d %d %d %d %d", l_y, l_m, l_d, l_hms[1], l_hms[2], l_hms[3]));
    if (line_time>tlimit) exit; lastline=$0;
  }
  END{if lastline=="" print $0; else print lastline;}' myfile.csv

它的工作原理是使用awk的时间函数mktime从每一行创建时间戳。我还假设$1是日期。

在第一行,您必须提供所需时间限制的时间戳(此处我选择2013 07 30 22 09 00)。您必须根据mktimeYYYY MM DD hh mm ss使用的格式编写它。您开始使用awk语句来编写时间限制的时间戳。然后,对于每一行,您可以从$1(第4行)追踪年,月和日,然后从$2(第5行)获取确切的小时。由于mktime仅需要整秒,我会截断秒数(您可以使用int(l_hms[3]+0.5)将其四舍五入)。在这里你可以做你想要接近时间戳的东西,比如丢弃秒。在第6行,我从我提取的六个日期字段中创建时间戳。最后,在第7行,我会在达到你的时间限制时比较时间戳和转到结尾。如您所希望的那样,我将该行存储到变量lastline中。退出时,我打印lastline;如果在第一行达到时间限制,我打印第一行。

此解决方案适用于您的示例文件,适用于您提供的任何日期。您只需要以正确的格式提供日期限制!

修改

我意识到mktime没有必要。如果假设$1是写为YYYYMMDD的日期,您可以将日期作为数字与时间进行比较(使用split提取,重建为数字,与其他答案一样)。在这种情况下,您可以提供所需格式的时间限制,并在BEGIN块中恢复正确的日期和时间限制。

答案 2 :(得分:1)

您可以使用awk代替您的grep

 awk -v FS=',' -v Hour=22 -v Min=9 '{split($2, a, "[:]"); if ((3600*a[1] + 60*a[2] + a[3] - 3600*Hour - 60*Min)^2 < 100) print $0}' file

并且基本上将100更改为您想要的容差。