У меня есть файл журнала, как это:

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

Но я не могу использовать сортировку для моего файла журнала. Что я могу сделать для am-pm? Как я могу сделать это с помощью sort, awk или другого?

1
phe 29 Дек 2015 в 15:59

6 ответов

Лучший ответ

Просто используйте 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
3
Ed Morton 29 Дек 2015 в 14:04

В чистом Perl на основе решения @ glennjackman:

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

Предполагая, что массив @_ содержит строки файла журнала. Здесь используется преобразование Шварца.

0
Community 23 Май 2017 в 10:28

Другой аналогичный подход с использованием Perl

perl -MTime::Piece -lpe '$_ = Time::Piece->strptime($_, "%B %d, %Y, %l:%M%p")->strftime("%s") . "\t" . $_' file | 
sort -n | 
cut -f2-
2
glenn jackman 29 Дек 2015 в 18:06

Я вспомнил, что отправил ответ на аналогичный вопрос. Однако после поиска я не могу его найти.

Итак, идея состоит в том, чтобы вычислить секунды после 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
2
Kent 29 Дек 2015 в 14:01

Вы все еще можете делать это поле за полем, разделяя составные

$ 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 <1 <2 <... для каждого меридиема (am / pm). Исправление заменяет 12 на 00 и возвращает обратно после сортировки.

$ 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

Пс. Теперь под сомнение выбранный формат логов.

1
karakfa 30 Дек 2015 в 17:51

Вы можете использовать инструменты 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
fedorqui 'SO stop harming' 29 Дек 2015 в 13:18