【问题标题】:awk filter rows based on date conditionawk 根据日期条件过滤行
【发布时间】:2020-12-04 19:29:56
【问题描述】:

我有一个用于分析的大型数据集,我正在寻找 shell 脚本以仅将行过滤到我需要的行,因此我能够加载数据集以在 R 中进行进一步分析。

数据结构如下:

Size,ModifiedTime,AccessTime,contentid
4886,"Jun 11, 2009 06:51:08 PM","Mar 15, 2013 09:24:53 AM",000000285b7925f511b3159a72f80a4a
4096,"Aug 21, 2008 03:54:28 PM","May 12, 2009 04:45:41 PM",0000011afae4d1227c4df57b410ea52c
84848,"Feb 12, 2007 12:40:00 PM","Apr 07, 2014 09:39:03 AM",000001cec02017ca3eb81ddc4cd1c9ff
518,"Aug 22, 2006 02:12:03 PM","Dec 25, 2007 06:48:18 AM",00000233565d1c17c3135a9504c455ca
264158,"Dec 08, 2009 03:28:14 PM","Apr 08, 2013 11:52:15 AM",000003020ba74b9d1b6075d3c1b8fcb3
725963,"Sep 29, 2008 03:45:21 PM","May 17, 2011 08:48:40 AM",0000034b98d29d84ce7b61ee68be7658
1340,"Sep 07, 2011 03:36:54 AM","Mar 12, 2013 02:55:01 AM",000004ed899e26ae1c9b1ece35a98af1
75264,"Jul 28, 2011 05:09:58 PM","Jun 07, 2014 04:21:28 PM",000005a09fd2eb706c5800eb06084160
198724,"Jul 23, 2012 02:25:58 PM","Jan 21, 2013 12:58:07 PM",0000060b9d552c35f281b5033dcfa1b4

它本质上是一个大的 csv 文件。

现在我想过滤 AccessTime 小于 10 年的行,然后将其写入一个单独的 csv 文件,在这种情况下应该打印第二行(不包括标题)

我尝试了以下方法:创建一个临时时间变量并与AccessTime 进行比较,如果小于则打印行。

BEGIN{
    FPAT = "([^,]+)|(\"[^\"]+\")"; #this to read csv as some column value contains ,
    OFS=",";
    date=$(date -d "-3650 days" +"%s"); #temp time variable in epoch format
}
{
    command="date -d" $6 " +%s"; #$6 refers to AccessTime column
    ( command | getline temp );  #converts Accesstime value to epoch format
    close(command);
    if(temp<date) print $6
}

但是当我运行这个命令时,它不会打印任何东西。 非常感谢任何帮助。

期望的输出:

Size,ModifiedTime,AccessTime,contentid
4096,"Aug 21, 2008 03:54:28 PM","May 12, 2009 04:45:41 PM",0000011afae4d1227c4df57b410ea52c
518,"Aug 22, 2006 02:12:03 PM","Dec 25, 2007 06:48:18 AM",00000233565d1c17c3135a9504c455ca

【问题讨论】:

    标签: shell awk


    【解决方案1】:
    $ awk '
    BEGIN {
        m["Jan"]="01"                                     # lookups for months
        m["Feb"]="02"                                     # Feb -> 02
        m["Mar"]="03"                                     # Mar -> 03
        m["Apr"]="04"                                     # etc.
        m["May"]="05"
        m["Jun"]="06"
        m["Jul"]="07"
        m["Aug"]="08"
        m["Sep"]="09"
        m["Oct"]="10"
        m["Nov"]="11"                                     # below we get todays date
        m["Dec"]="12"                                     # 10 years ago
    
        dcmd="date +\"%Y%m%d,\" --date=\"10 years ago\""  # returns 20101204,
        if((dcmd | getline d)<=0)                         # if getline fails
            exit 1                                        # exit
    
        # d=strftime("%Y%m%d")-10^5 ","                   # use this for GNU awk
    }
    $9 m[$7] $8>=d' file                                  # explained below
    

    ddate +"%Y%m%d," --date="10 years ago" 获取值20101204.(注意结尾的逗号)。从文件中读取 AccessTime 并使用 $9 m[$7] $8 重新排列组件,例如,Mar 15, 201320130315,(再次注意逗号)。条件是这两个日期的比较。

    输出:

    4886    Jun 11, 2009 06:51:08 PM        Mar 15, 2013 09:24:53 AM        000000285b7925f511b3159a72f80a4a
    84848   Feb 12, 2007 12:40:00 PM        Apr 07, 2014 09:39:03 AM        000001cec02017ca3eb81ddc4cd1c9ff
    264158  Dec 08, 2009 03:28:14 PM        Apr 08, 2013 11:52:15 AM        000003020ba74b9d1b6075d3c1b8fcb3
    725963  Sep 29, 2008 03:45:21 PM        May 17, 2011 08:48:40 AM        0000034b98d29d84ce7b61ee68be7658
    1340    Sep 07, 2011 03:36:54 AM        Mar 12, 2013 02:55:01 AM        000004ed899e26ae1c9b1ece35a98af1
    75264   Jul 28, 2011 05:09:58 PM        Jun 07, 2014 04:21:28 PM        000005a09fd2eb706c5800eb06084160
    198724  Jul 23, 2012 02:25:58 PM        Jan 21, 2013 12:58:07 PM        0000060b9d552c35f281b5033dcfa1b4
    

    【讨论】:

    • 能否请您解释一下您的代码,对不起,我是 awk 的初学者,很难理解代码?
    • 非常感谢@James Brown
    • 嗨,James,date 命令无需重新排列组件即可工作。你能看看我的回答吗?
    【解决方案2】:

    使用 GNU awk 处理时间函数、FPAT 和 gensub():

    $ cat tst.awk
    BEGIN {
        OFS  = ","
        FPAT = "([^" OFS "]*)|(\"[^\"]+\")"
        now  = strftime("%Y %m %d %H %M %S")
        year = gensub(/ .*/,"",1,now)
        rest = gensub(/[^ ]+/,"",1,now)
        secs = mktime((year-10) rest)
        thresh = strftime("%Y%m%d%H%M%S",secs)
    }
    NR > 1 {
        split($3,t,/[ ,:]+/)
        mthNr = (index("JanFebMarAprMayJunJulAugSepOctNovDec",t[1])+2)/3
        hour  = t[4] + ( (t[7] == "PM") && (t[4] < 12) ? 12 : 0 )
        curr  = sprintf("%04d%02d%02d%02d%02d%02d", t[3], mthNr, t[2], hour, t[5], t[6])
    }
    (NR == 1) || (curr < thresh)
    

    $ awk -f tst.awk file
    Size,ModifiedTime,AccessTime,contentid
    4096,"Aug 21, 2008 03:54:28 PM","May 12, 2009 04:45:41 PM",0000011afae4d1227c4df57b410ea52c
    518,"Aug 22, 2006 02:12:03 PM","Dec 25, 2007 06:48:18 AM",00000233565d1c17c3135a9504c455ca
    

    【讨论】:

    • 非常感谢@EdMorton,您的解决方案非常有效。珍惜你的时间
    【解决方案3】:

    基于您展示的示例,使用 GNU awk 中的展示示例编写和测试。还考虑到您需要将第二次出现的日期与当前日期进行比较。此解决方案也没有处理闰秒概念(在这里也试图找出另一种方法)。

    awk '
    BEGIN{
      num=split("jan,feb,mar,apr,may,jun,jul,aug,sept,oct,nov,dec",arr1,",")
      for(i=1;i<=num;i++){
        month[arr1[i]]=sprintf("%02d",i)
      }
    }
    match($0,/[AP]M.*[AP]M/){
      val=substr($0,RSTART,RLENGTH)
      sub(/^[AP]M +/,"",val)
      sub(/ [AP]M +$/,"",val)
      split(val,array,"[ ,]")
      dat=array[4] OFS month[tolower(array[1])] OFS array[2] OFS array[5]
      timE=(systime()-mktime(gensub(/[ ":-]/," ","g",dat)))/(365*60*24*60)
      if(timE>10){ print }
    }
    ' Input_file
    

    这不会打印标题以防您需要打印然后在match函数之前添加FNR==1{print;next}

    【讨论】:

      【解决方案4】:

      另一个更短的 awk 解决方案。

      $ awk -F, -v ct=$(date "+%s") ' NR>1 { dc="date -d"$4 $5 " \"+%s\""; dc|getline t;  yrs=(ct - t)/(24*60*60*365) }  yrs>10 || NR==1 ' monte.txt
      Size,ModifiedTime,AccessTime,contentid
      4096,"Aug 21, 2008 03:54:28 PM","May 12, 2009 04:45:41 PM",0000011afae4d1227c4df57b410ea52c
      518,"Aug 22, 2006 02:12:03 PM","Dec 25, 2007 06:48:18 AM",00000233565d1c17c3135a9504c455ca
      $
      

      解释:

      如果我们只传递访问时间的字符串表示,date 命令就可以工作。

      $ date -d"Jun 11, 2009 06:51:08 PM"
      Thu Jun 11 18:51:08 IST 2009
      

      即使没有逗号也可以使用

      $ date -d"Jun 11 2009 06:51:08 PM"
      Thu Jun 11 18:51:08 IST 2009
      

      因此无需清理数据。只需从输入文件中以逗号作为分隔符传递 $4 和 $5 即可。

      为了比较,我用的是纪元

      awk -F, -v ct=$(date "+%s") '           #get the current epoch seconds via ct
      NR>1 { 
             dc="date -d"$4 $5 " \"+%s\"";    #build the date command using access time $4 and %5
             dc|getline t;                    #execute the command and get the output in temp t
             yrs=(ct - t)/(24*60*60*365)      #calcualte the number of years between ct and t
       }  
      
       yrs>10 || NR==1                         #print if diff yrs > 10 or NR==1 for header
      '
      

      另一种解决方案:

      如果你想在 date 命令中应用 10 年的逻辑,那么我们只需删除 $5 中的双引号即可。

      $ awk -F, -v ct=$(date "+%s") ' NR>1 { c5=substr($5,1,length($5)-1);dc="date -d"$4 c5 " + 10 years \" \"+%s\""; dc|getline t } t<ct  ' monte.txt
      Size,ModifiedTime,AccessTime,contentid
      4096,"Aug 21, 2008 03:54:28 PM","May 12, 2009 04:45:41 PM",0000011afae4d1227c4df57b410ea52c
      518,"Aug 22, 2006 02:12:03 PM","Dec 25, 2007 06:48:18 AM",00000233565d1c17c3135a9504c455ca
      $
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-10-24
        • 2011-12-04
        • 2020-03-31
        • 2020-02-15
        • 1970-01-01
        • 2018-11-25
        相关资源
        最近更新 更多