【问题标题】:How can I sort by weird date format?如何按奇怪的日期格式排序?
【发布时间】:2016-04-03 08:52:58
【问题描述】:

我有一个这样的日志文件:

December 20, 2015, 11:00pm
November 18, 2014, 12:00am
October 05, 2012, 11:30pm
October 02, 2012, 5:30pm
October 01, 2012, 12:30am
October 01, 2010, 11:30am
October 01, 2011, 9:30pm
October 01, 2011, 7:30am
...

我可以对简单的日期格式使用排序,如下所示:

Mar  4 07:45
Mar  8 06:45
Mar  8 05:45

sort -k1M -k2 -k3 text.txt

Mar  4 07:45
Mar  8 05:45
Mar  8 06:45

但是我不能对我的日志文件使用排序。我可以为上午下午做什么?我如何使用sortawk 或其他方式做到这一点?

【问题讨论】:

  • 您能否提供一个更广泛的案例来查看问题的所有角落和边缘?
  • 在您的示例中,日志行的顺序完全相反(最新的在顶部)。如果总是这样,那么您可以使用tac 来反转行的顺序。
  • 不不,这只是一个例子。当然并非总是如此。这就是为什么我需要对月、日、年和小时/分钟/ - am/pm 进行排序。

标签: bash perl sorting awk sed


【解决方案1】:

只需使用 awk 从每个输入行创建一个 YYYYMMDDHHMM 字符串并将其添加到每行以进行输出,然后通过管道对其进行排序,然后剪切以删除 awk 预先添加的字符串:

$ cat tst.awk
BEGIN { FS="(,? +|:)" }
{
    mthAbbr = substr($1,1,3)
    mthNr = (match("JanFebMarAprMayJunJulAugSepOctNovDec",mthAbbr)+2)/3
    ampm = $NF; sub(/.*[0-9]/,"",ampm)
    hour = $4 + ( (ampm=="pm") && ($4<12) ? 12 : 0 )
    printf "%04d%02d%02d%02d%02d\t%s\n", $3, mthNr, $2, hour, $5, $0
}

$ awk -f tst.awk file | sort | cut -f2-
October 01, 2010, 11:30am
October 01, 2011, 7:30am
October 01, 2011, 9:30pm
October 01, 2012, 12:30am
October 02, 2012, 5:30pm
October 05, 2012, 11:30pm
November 18, 2014, 12:00am
December 20, 2015, 11:00pm

为了帮助您了解正在发生的事情,以下是中间步骤:

$ awk -f tst.awk file
201512202300    December 20, 2015, 11:00pm
201411181200    November 18, 2014, 12:00am
201210052330    October 05, 2012, 11:30pm
201210021730    October 02, 2012, 5:30pm
201210011230    October 01, 2012, 12:30am
201010011130    October 01, 2010, 11:30am
201110012130    October 01, 2011, 9:30pm
201110010730    October 01, 2011, 7:30am

$ awk -f tst.awk file | sort
201010011130    October 01, 2010, 11:30am
201110010730    October 01, 2011, 7:30am
201110012130    October 01, 2011, 9:30pm
201210011230    October 01, 2012, 12:30am
201210021730    October 02, 2012, 5:30pm
201210052330    October 05, 2012, 11:30pm
201411181200    November 18, 2014, 12:00am
201512202300    December 20, 2015, 11:00pm

【讨论】:

  • 我测试了 Ed Morton 的 awk、Fedorqui 的 bash、Glenn Jackman 的 perl 解决方案,一切正常。我接受 Ed Morton 的回答,因为最快(200k 行)。谢谢大家!
【解决方案2】:

您可以使用 Bash 工具将日期转换为时间戳、添加此信息、排序并将其删除:

while IFS=, read -r day year hour; do
   printf "%s %s, %s, %s\n" "$(date -d"$day $year $hour" +"%s")" "$day" "$year" "$hour"
done < file  | sort -n | cut -d' ' -f2-

假设格式为day, year, hour 格式。

一步一步

让我们将日期转换为时间戳:

while IFS=, read -r day year hour;
do
printf "%s %s, %s, %s\n" "$(date -d"$day $year $hour" +"%s")" "$day" "$year" "$hour"
done < a                            
1450648800 December 20,  2015,  11:00pm
1416265200 November 18,  2014,  12:00am
1349472600 October 05,  2012,  11:30pm
1349191800 October 02,  2012,  5:30pm
1349044200 October 01,  2012,  12:30am
1285925400 October 01,  2010,  11:30am
1317497400 October 01,  2011,  9:30pm

让我们排序:

while IFS=, read -r day year hour;
do
printf "%s %s, %s, %s\n" "$(date -d"$day $year $hour" +"%s")" "$day" "$year" "$hour"
done < a  | sort -n                 
1285925400 October 01,  2010,  11:30am
1317497400 October 01,  2011,  9:30pm
1349044200 October 01,  2012,  12:30am
1349191800 October 02,  2012,  5:30pm
1349472600 October 05,  2012,  11:30pm
1416265200 November 18,  2014,  12:00am
1450648800 December 20,  2015,  11:00pm

让我们删除临时时间戳:

$ while IFS=, read -r day year hour;
do
printf "%s %s, %s, %s\n" "$(date -d"$day $year $hour" +"%s")" "$day" "$year" "$hour"
done < a  | sort -n | cut -d' ' -f2-
October 01,  2010,  11:30am
October 01,  2011,  9:30pm
October 01,  2012,  12:30am
October 02,  2012,  5:30pm
October 05,  2012,  11:30pm
November 18,  2014,  12:00am
December 20,  2015,  11:00pm

【讨论】:

    【解决方案3】:

    我记得我已经发布了一个类似问题的答案。但是搜索后我找不到它。

    所以想法是计算 1970-01-01 之后的秒数,并将其作为前缀添加到您的原始行,然后进行排序,最后删除前缀字段。

    awk -v cmd='date -d"%s" +%s' 
       '{o=$0;gsub(/,/,"");cc=sprintf(cmd,$0,"%s");
         cc|getline d
         close(cc);print d"\x99"o}' file|sort -n|sed 's/.*\x99//'
    

    \x99 是一个不可见的字符,只是为了确保它不会与文件中的现有字符冲突。

    输入示例的输出:

    October 01, 2010, 11:30am
    October 01, 2011, 7:30am
    October 01, 2011, 9:30pm
    October 01, 2012, 12:30am
    October 02, 2012, 5:30pm
    October 05, 2012, 11:30pm
    November 18, 2014, 12:00am
    December 20, 2015, 11:00pm
    

    【讨论】:

      【解决方案4】:

      另一种类似的方法,使用 Perl

      perl -MTime::Piece -lpe '$_ = Time::Piece->strptime($_, "%B %d, %Y, %l:%M%p")->strftime("%s") . "\t" . $_' file | 
      sort -n | 
      cut -f2-
      

      【讨论】:

      • 它的工作,但首先得到这样的错误:at /usr/lib/perl/5.18/Time/Piece.pm line 469, &lt;&gt; line 1.
      • 你不能将排序/剪切构建到perl吗?
      • @Sobrique 当然可以,但它会更冗长,更难维护,而且很可能更慢。
      【解决方案5】:

      您仍然可以通过分隔复合字段来逐个字段地进行操作

      $ sed 's/[ap]m/ &/;s/:/ : /' log \
         | sort -k3,3 -k1,1M -k2,2 -k7 -k4,4n -k6,6 \
         | sed -r 's/ : /:/;s/ ([ap]m)/\1/'
      
      October 01, 2010, 11:30am
      October 01, 2011, 7:30am
      October 01, 2011, 9:30pm
      October 01, 2012, 12:30am
      October 02, 2012, 5:30pm
      October 05, 2012, 11:30pm
      November 18, 2014, 12:00am
      December 20, 2015, 11:00pm
      

      更新:由于罗马人没有 0,我们有 12

      $ sed 's/[ap]m/ &/;s/12:/00:/;s/:/ : /' log \
          | sort -k3,3 -k1,1M -k2,2 -k7 -k4,4n -k6 \
          | sed -r 's/ : /:/;s/ ([ap]m)/\1/;s/00:/12:/' 
      
      October 01, 2010, 11:30am
      October 01, 2011, 7:30am
      October 01, 2011, 9:30pm
      October 01, 2012, 12:30am
      October 02, 2012, 5:30pm
      October 05, 2012, 11:30pm
      November 18, 2014, 12:00am
      November 18, 2015, 12:00am
      November 18, 2015, 1:00am
      November 18, 2015, 12:00pm
      November 18, 2015, 1::00pm
      December 20, 2015, 11:00pm
      

      ps。现在质疑为日志选择的格式。

      【讨论】:

      • 我测试过,但它不起作用。添加November 18, 2015, 12:00am,可以再次测试。
      【解决方案6】:

      基于@glennjackman's的纯Perl解决方案:

      say $_->[1] for sort {$a->[0] <=> $b->[0]}
      map [Time::Piece->strptime($_, "%B %d, %Y, %l:%M%p")->strftime("%s"), $_], @_;
      

      假设数组@_ 包含日志文件的行。这使用Schwartzian transform

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-02
        • 1970-01-01
        • 1970-01-01
        • 2013-02-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多