2

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

我的文件.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 中的确切时间戳一起工作。有没有办法让它将这些视为时间序列?(我猜这就是问题所在 - 它正在尝试纯模式匹配,而不是无理地找不到一个)

4

3 回答 3

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)。您必须根据mktime:使用的格式编写它YYYY MM DD hh mm ss。您开始 awk 语句,以弥补您的时间限制的时间戳。然后,对于每一行,您从(第 4 行)赶上年、月和日$1,然后从(第 5 行)赶上确切的小时$2。由于mktime只需要整秒,我会截断秒数(您可以用 将其四舍五入int(l_hms[3]+0.5))。在这里你可以做任何你想要近似时间戳的事情,比如丢弃秒数。在第 6 行,我从提取的六个日期字段中制作时间戳。最后,在第 7 行,我比较了时间戳,并在达到您的时间限制时结束。如您所愿,我将该行存储到变量中lastline. 退出时,我打印lastline;如果达到第一行的时间限制,我会打印第一行。

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

编辑

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

于 2013-08-23T15:52:58.893 回答
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, Spain 和 APPLES 喂它,它返回预期的“20130730,22:08:51.244,APPLES,Spain,67p,blah,blah”行)

如果您的文件跨越几天(解决 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

如果第一列发生变化(即,如果它跨越几天),最后一个也可以工作,但你也需要在当天喂它

于 2013-08-23T14:54:27.180 回答
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您想要的任何容忍度。

于 2013-08-23T14:10:33.353 回答