Bash查找时间戳之前的最后一个条目

Bash查找时间戳之前的最后一个条目,bash,grep,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

我有一个.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中使用精确的时间戳。有没有办法让它把这些数据当作一个时间序列?(我猜这就是问题所在-它尝试纯模式匹配,而不是无理地找不到模式匹配)

您可以使用
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
awk-vfs=','-vhour=22-vmin=9'{split($2,a,“[:]”);if((3600*a[1]+60*a[2]+a[3]-3600*Hour-60*Min)^2<100)打印$0}文件

基本上,将
100
更改为您想要的公差。

您可以使用awk在内存中保留它看到的最后一行,该行的时间戳低于您输入的那一行,并在末尾打印最后一个匹配项(考虑到它们是按升序排列的)

例:

awk-vfs=','-v thetime=“22:09”($2
当您向它输入一个字符串时,这恰好起作用,从图形上看,该字符串不需要具有要比较的完整大小(即22:09:00.000)

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

awk  -v FS=',' -v thetime="22:09" '
   ($2 < thetime) { before=$0 ; }  
   END            { print before ; }' myfile.csv
awk-vfs=','-v thetime=“22:09”'
($2
现在,如果我理解了您的全部要求:您需要在标记国家和产品类型的行中,找到时间戳之前的最后一行?然后:

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
awk-vfs=','-v thetime=“${timestamp}”-v country=“${thecountry}”-v product=“${theproduct}”
($4==国家)&&($3==产品)&&($2<时间){before=$0;}
结束{print before;}'myfile.csv
应该为你工作。。。(输入10:07,西班牙和苹果,它将返回预期的“20130730,22:08:51.244,苹果,西班牙,67p,blah,blah”行)

如果你的文件跨越几天(为了解决Bentoy13的问题)

awk-v FS=','-v theday=“${theday}”-v thetime=“${timestamp}”-v thecountry=“${thecountry}”-v theproduct=“${theproduct}”

($4==国家)&&($3==产品)&&($1我还有一个使用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的时间函数从每行生成时间戳。我还假设
$1
是日期

在第一行,您必须提供所需时间限制的时间戳(这里我选择
2013 07 30 22 09 00
)。您必须按照
mktime
使用的格式编写它:
YYYY-MM-DD-hh-MM-ss
。您在开始awk语句时,先填写时间限制的时间戳。然后,对于每一行,您从
$1
开始计算年、月、日(第4行),然后从
$2
开始计算准确的小时(第5行)。在这里,你可以做任何你想要近似时间戳的事情,比如丢弃秒数。在第6行,我从我提取的六个日期字段中创建时间戳。最后,在第7行,我比较时间戳,并在达到时间限制时转到结束。正如你想要前一行一样,我将该行存储到变量
lastline。退出时,我打印
lastline
;如果第一行达到时间限制,我打印第一行

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

编辑


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

注意,您正在查找“西班牙”,而文本是“西班牙”。谢谢-我不太准确,但仅在这里的帖子中。我将修改并澄清您不清楚的地方。如果我执行此命令,它将返回预期的输出+上一行。即,
22:09:62.108
22:08:51.244
。我已将最后两个时间戳修改为22:10*,以强调这一点。我明白为什么您成功地找到了与此数据匹配的数据。您可能已经猜到,我使用的数据是一个虚构的示例,因为实际数据是机密的。很抱歉浪费您的时间。@Pascoe:我读到的版本不清楚:您希望在22:09之前有一行,但您说您希望有一行在上面3行(也就是说,你为什么期望22:08:51.244,苹果,而22:09之前的那一行是22:08:51.708,苹果?)谢谢。如果可能的话,尽量避免在awk中使用,因为我觉得语法很笨拙。我相信如果你一直使用它会很好,但这里的可能用例是它作为一行从R studio内部传递过来,所以awk工作得不太好。尽管如此,回答还是很好。对于awk来说+1,因为它可能是最简洁但可读性很强的版本。什么scoe想要(使用grep,但搜索词可能不包含在任何行中)不可理解:grep显然不适合这种情况。同意-我说过我理解它是一个模式匹配命令。我试图在bash中找到具有类似语法的命令来处理它。我知道在bash眼中它不是时间戳,但同样我知道我可以使用sed获取文件的一部分,所以我对此充满希望可能会有一些妥协。从我的帖子中你可以看出,我是这方面的业余爱好者。@Pascoe-来自
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
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