【问题标题】:Sort Date/Time in Unix在 Unix 中排序日期/时间
【发布时间】:2012-07-23 15:07:09
【问题描述】:

输入

2012-07-24 10:05:08 AM
2012-07-26 10:13:58 AM
2012-07-24 10:13:58 AM
2012-07-24 10:57:50 AM
2012-07-24 11:15:03 AM
2012-07-24 11:26:08 PM
2012-07-25 11:26:08 PM

期望的输出

2012-07-24 10:05:08 AM
2012-07-24 10:13:58 AM
2012-07-24 10:57:50 AM
2012-07-24 11:15:03 AM
2012-07-24 11:26:08 PM
2012-07-25 11:26:08 PM
2012-07-26 10:13:58 AM

我试过的代码

 sort -t ":" -k 1 -k 2 -k 3 Input.txt | sort -t " " -k 3

但我没有得到想要的输出。

谁能给点建议?


我写了一个代码...但问题仍然存在...

代码

 sed 's/ 12:/00:/g' Input.txt | sort -k 1,1 -k 3,3 -k 2,2 | sed 's/00:/12:/g'

首先将 12:43:01 AM 更改为 00:43:01 AM....然后应用排序命令。

【问题讨论】:

  • 什么想要的输出?
  • 在上午之前下午?这真的很需要吗?
  • @steffen 根据时间戳....(日期、时间、上午/下午)
  • 有点困惑,在输入中我看到一行:2012-07-24 10:13:58 PM,在输出中我看到该行更改为:2012-07-24 10:13:58 AM
  • @Jon 对不起我的错误......我编辑了所需的输出......

标签: perl bash shell sed awk


【解决方案1】:

最后我在不使用任何外部模块的情况下进行了编码。虽然它很长,但适用于任何日期格式。

使用的技术:

  1. 首先将文件中的每个日期转换为其时间戳
  2. 按数字对时间戳进行排序。
  3. 再次使用标量本地时间将时间戳转换为日期。

代码

my @input = `cat Input.txt`;

    open (ts,">","tt.txt");
    foreach my $i (@input)
    {
            chomp($i);
            my $timestamp = `date --date "$i" +\%s`;
            chomp($timestamp);
            push (@time,$timestamp);
            print ts "$timestamp\n";
    }
    close(ts);

    open (ts,">","sort_time.txt");
    my @sorted_time = join "\n",sort {$a<=>$b} @time;
    chomp(@sorted_time);
    print ts "@sorted_time\n";
    close(ts);

    my @input1=  `cat sort_time.txt`;
    open (ts,">","sort_timestamp.txt");
    foreach my $st1 (@input1)
    {
            chomp($st1);
            my $st2 = scalar localtime($st1);
            chomp($st2);
            print ts "$st2\n";
    }
    close(ts);



    @input2 = `cat sort_timestamp.txt`;
    open (ts,">","Output.txt");
    foreach my $st2 (@input2)
    {
            chomp($st2);
            $pro_time = `date --date "$st2" +\%Y-\%m-\%d~\%r | sed 's/~/ /g'`;
            chomp($pro_time);
            print ts "$pro_time\n";
    }
    close(ts);


    `rm tt.txt sort_time.txt sort_timestamp.txt`;

【讨论】:

  • 您决定在 perl 中重新实现 date 实用程序的任何特殊原因是什么?
  • 这非常低效,我认为 CodeGnome 和其他人已经给出的答案没有任何好处。如果您的工作环境有限制,您应该将它们发布在原始问题中。事实上,你的 perl 代码中的大部分工作都是通过 shell 完成的,其余的也在 shell 中更有效地处理,那么为什么要使用 perl 呢?如果这必须在 perl 中完成,您可以在一次使用反引号时使用完美的 bash 答案之一。
  • @chepner 我同意你的看法...应该发布我的限制...我下次会注意...谢谢!!
【解决方案2】:

你可以用这个:

sed 's/ 12:/ 00:/'| LC_ALL="C" sort -k 1,1 -k 3 | sed 's/ 00:/ 12:/'

这应该是相当快的解决方案。

【讨论】:

  • 好吧,那怎么样?确实如此! -h 选项相对较新:我知道事实上 Ubuntu 10.04 (Lucid Lynx) 没有它。
  • @ams 你是对的,它是一个相当新的功能,它甚至不起作用。我已经做了一个新的,它绝对有效。
  • 您的新解决方案似乎按日期和上午/下午排序,但不是时间?
  • 顺便说一句,鉴于-h 与 AM/PM 配合得非常好,我怀疑不与 12
  • @ams: 使用--debug 排序参数,您将看到 sort 自动添加整行排序作为最后一个排序规则,因此它将在最后一个规则中按时间排序,因为键 1(日期)是已经在那里了。
【解决方案3】:

这可能对你有用(GNU sed):

sed 's/.*/echo -e "$(date -d"&" +%s)\t&"/e' file | sort -n | sed 's/.*\t//'

或:

date -f file +%s | paste - file | sort -n | sed 's/\S\+\s\+//'

【讨论】:

    【解决方案4】:

    转换为纪元秒数进行排序

    假设您的数据存储在 /tmp/foo 中,您可以使用 GNU 日期将时间戳转换为可数字排序的格式。例如:

    date -f /tmp/foo '+%s' | sort |
    while read; do
        date -d "@$REPLY" "+%F %I:%M:%S %p"
    done
    

    这应该在所有情况下正确处理排序,尤其是在同一日期所有上午时间都应该在所有下午时间之前的情况。例如,现在在晚上 10:00 之前列出了 12:01 AM。

    【讨论】:

    • 或跳过 while 循环:date --file /tmp/foo +%s | sort | sed 's/^/@/' | date --file - "+%F %I:%M:%S %p"sed 有点小瑕疵,但 c'est la vie
    【解决方案5】:

    使用Schartzian TransformDate::Parse

    use strict;
    use warnings;
    use 5.010;
    use Date::Parse;
    
    my @data = <DATA>;
    chomp @data;
    
    my @sorted = 
        map  { $_->[0] }
        sort { $a->[1] <=> $b->[1] }
        map  { [$_, str2time($_)] } @data;
    
    say for @sorted;
    
    __DATA__
    2012-07-24 11:15:03 AM
    2012-07-24 11:26:08 PM
    2012-07-25 01:26:08 PM
    2012-07-25 12:26:08 PM
    2012-07-25 01:26:08 AM
    2012-07-25 12:26:08 AM
    2012-07-25 11:26:08 AM
    2012-07-25 11:26:08 PM
    

    输出:

    2012-07-24 11:15:03 AM
    2012-07-24 11:26:08 PM
    2012-07-25 12:26:08 AM
    2012-07-25 01:26:08 AM
    2012-07-25 11:26:08 AM
    2012-07-25 12:26:08 PM
    2012-07-25 01:26:08 PM
    2012-07-25 11:26:08 PM
    

    【讨论】:

    • 比较运算符应该是&lt;=&gt;,因为str2time返回一个POSIX时间值。出于同样的原因,您的解决方案不适用于 1970 年之前的日期。
    • @Borodin:你说得对,我换了运营商。它不适用于 1970 年之前的日期,但我将答案留给 1969 年之后的日期使用
    【解决方案6】:

    字符串可以简单地按词法排序,除了 12 小时时间。

    此解决方案使用Schwartzian Transform 更改用于对字符串进行排序的键。它只是将 12 添加到以 PM 结尾的任何字符串的小时字段中,然后按此排序。

    use strict;
    use warnings;
    
    my @data = <DATA>;
    chomp @data;
    
    my @sorted = map $_->[0],
    sort { $a->[1] cmp $b->[1] }
    map { (my $dt = $_) =~ s/(\d\d)(?=:\d\d:\d\d PM)/$1+12/e; [$_, $dt] } @data;
    
    print "$_\n" for @sorted;
    
    
    __DATA__
    2012-07-24 10:05:08 AM
    2012-07-26 10:13:58 AM
    2012-07-24 10:13:58 AM
    2012-07-24 10:57:50 AM
    2012-07-24 11:15:03 AM
    2012-07-24 11:26:08 PM
    2012-07-25 11:26:08 PM
    

    输出

    2012-07-24 10:05:08 AM
    2012-07-24 10:13:58 AM
    2012-07-24 10:57:50 AM
    2012-07-24 11:15:03 AM
    2012-07-24 11:26:08 PM
    2012-07-25 11:26:08 PM
    2012-07-26 10:13:58 AM
    

    更新

    正如 steffen 所指出的,即使在调整了上午/下午的时间之后,午夜和中午仍然会阻止简单的字符串排序工作。

    该程序使用核心 Time::Piece 模块将日期/时间重新格式化为 ISO 8601 格式 2000-02-29T12:34:56可以按词法排序。

    use strict;
    use warnings;
    
    use Time::Piece;
    
    my @data = <DATA>;
    chomp @data;
    
    my @sorted = map $_->[0],
    sort { $a->[1] cmp $b->[1] }
    map { [ $_, toISO8601($_) ] } @data;
    
    sub toISO8601 {
      Time::Piece->strptime(@_, '%Y-%m-%d %I:%M:%S %p')->datetime;
    }
    
    print "$_\n" for @sorted;
    
    __DATA__
    2012-07-24 10:05:08 AM
    2012-07-26 10:13:58 AM
    2012-07-24 10:13:58 AM
    2012-07-24 10:57:50 AM
    2012-07-24 11:15:03 AM
    2012-07-24 11:26:08 PM
    2012-07-25 11:26:08 PM
    2012-08-01 01:00:00 PM
    2012-08-01 12:30:00 PM
    2012-08-01 12:00:00 PM
    2012-08-01 11:30:00 AM
    2012-08-01 01:00:00 AM
    2012-08-01 12:30:00 AM
    2012-08-01 12:00:00 AM
    

    输出

    2012-07-24 10:05:08 AM
    2012-07-24 10:13:58 AM
    2012-07-24 10:57:50 AM
    2012-07-24 11:15:03 AM
    2012-07-24 11:26:08 PM
    2012-07-25 11:26:08 PM
    2012-07-26 10:13:58 AM
    2012-08-01 12:00:00 AM
    2012-08-01 12:30:00 AM
    2012-08-01 01:00:00 AM
    2012-08-01 11:30:00 AM
    2012-08-01 12:00:00 PM
    2012-08-01 12:30:00 PM
    2012-08-01 01:00:00 PM
    

    【讨论】:

    • 另外,List::UtilsBy::nsort_by() 可能会帮助你,而不是 map/sort/map 三元组。 nsort_by { Time::Piece->strptime($_,'%Y-%m-%d %I:%M:%S %p')->epoch } @data
    【解决方案7】:

    有我的变种:

    $sed 's|\([0-9]\+\)-\([0-9]\+\)-\([0-9]\+\) \([0-9]\+\):\([0-9]\+\):\([0-9]\+\) \([A-Z]\+\)|\1 \2 \3 \4 \5 \6 \7 \0|' input.txt | awk '{if($7=="AM"){$7="1";if($4==12){$4 = 0}}else{$7="0"};print}' | sort -n -k1 -k2 -k3 -k4 -k5 -k6 -k7 | cut -d' ' -f 8-
    2012-07-24 10:05:08 PM
    2012-07-24 10:13:58 AM
    2012-07-24 10:57:50 AM
    2012-07-24 11:15:03 AM
    2012-07-24 11:26:08 PM
    2012-07-25 11:26:08 PM
    2012-07-26 10:13:58 AM
    

    添加额外字段的主要思想,按它们排序,排序后我去掉它们。数字排序很简单,但要对 AM/PM 进行排序,我会将其转换为 1/0 数字以简化排序。

    更新:sed + awk 用法可能被awk 取代:

    awk -F'[-: ]' '{printf("%d %d %d %d %d %d %d %s\n", $1, $2, $3, ($4 == 12 && $7 == "AM" ? 0 : $4), $5, $6, $7 == "AM", $0)}' input.txt |
    sort -n -k1 -k2 -k3 -k4 -k5 -k6 -k7 |
    cut -d' ' -f 8-
    

    更新:修复上午/下午问题

    【讨论】:

    • 嘿...我使用了您的概念...但以其他方式...我将上午 12:01 更改为上午 00:21 并应用了排序命令,然后 agian 将 00 替换为 12.. .. 谢谢:)
    • -1 因为它不起作用...12:05:50 AM 排在11:26:08 PM 之后
    • 现在10:57:50 AM 排在10:13:58 PM 之后;) 你到了那里。我知道那是什么感觉……
    • 据我了解这是作者的要求?
    【解决方案8】:

    有点awkward,我承认...

    猫输入.txt | \ awk 'BEGIN{FS="[: -]"}{if($7 == "PM") $4+=12;打印 $1"-"$2"-"$3" "$4":"$5":"$6" "$7}'|\ 排序|\ awk 'BEGIN{FS="[: -]"}{if($7 == "PM") $4-=12;打印 $1"-"$2"-"$3" "$4":"$5":"$6" "$7}'

    编辑:

    cat Input.txt |\
    awk 'BEGIN{FS="[: -]"}{if(length($4)==1) $4="0"$4 ;if($7 == "PM") $4+=12; else if($4 ==12)$4-=12; print $1"-"$2"-"$3" "$4":"$5":"$6" "$7}'|\
    sort|\
    awk 'BEGIN{FS="[: -]"}{if($7 == "PM") $4-=12; else if($4 ==0)$4+=12; print $1"-"$2"-"$3" "$4":"$5":"$6" "$7}'
    

    但它确实有效......

    说明:我使用awk将时间格式转换为24小时,排序再转换回来。

    编辑:我将 0 添加到只有一位数字的小时前,以便正确排序 1:0:012:0:0。也适用于上午。

    【讨论】:

    • 但它无法区分同一日期的凌晨 1:42 和 12:05....无法排序
    【解决方案9】:

    12:01 AM 早于 1:01 AM,所以我看不出仅使用 sort 会有什么帮助。

    您需要转换为其他格式,例如ISO 8601 或秒后 得到可以比较为文本或数字的东西。 perl oneliner 可以做到这一点。

    【讨论】:

    • 同意,如果没有这个问题,你会这样做:sort -k 1,1 -k 3,3 -k 2,2 Input.txt
    猜你喜欢
    • 2014-05-06
    • 2018-10-18
    • 1970-01-01
    • 1970-01-01
    • 2011-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-02
    相关资源
    最近更新 更多