4

输入

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 ....然后应用排序命令。

4

9 回答 9

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。

于 2012-07-25T13:15:13.813 回答
3

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

此解决方案使用Schwartzian 变换来更改用于对字符串进行排序的键。它只是将 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
于 2012-07-25T10:10:15.733 回答
2

有点awk病房,我承认...

猫输入.txt | \ awk 'BEGIN{FS="[: -]"}{if($7 == "PM") $4+=12; print $1"-"$2"-"$3" "$4":"$5":"$6" "$7}'|\ sort|\ awk 'BEGIN{FS="[: -]"}{if($7 == "下午") $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}'

但它的工作...

解释:我使用 将时间格式转换为 24 小时awk,对其进行排序并转换回来。

编辑:我在一个小时前加上一个数字,0以便正确排序。也适用于 AM。1:0:012:0:0

于 2012-07-25T09:11:56.580 回答
2

使用 夏兹变换Date::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
于 2012-07-25T12:52:21.900 回答
1

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

您需要转换为其他一些格式,例如 ISO 8601 或 seconds since,以获得可以作为文本或数字进行比较的内容。perl oneliner 可以做到这一点。

于 2012-07-25T08:32:50.423 回答
1

你可以使用这个:

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

它应该是非常快速的解决方案。

于 2012-07-25T22:14:40.487 回答
0

有我的变种:

$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-

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

于 2012-07-25T09:43:29.787 回答
0

这可能对您有用(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\+//'
于 2012-07-25T15:24:59.713 回答
-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`;
于 2012-07-27T07:01:18.797 回答