3

I'm new to scripting in bash and our professor had us sort a file in this format

peas|10.00|05 Apr 2012
pea soup|10.00|05 Jan 2012
ham|10.00|06 Apr 2012

using the date on the third field, with the most recent item appearing on top. I've tried using combinations of filters and sort but they didn't work. Can anyone help me? thanks

4 Answers 4

16

try

sort  -t '|' -k 3.8,3.11nr  -k 3.4,3.6Mr -k 3.1,3.2nr < input
      ------ -------------  ------------ ------------
      sep    first key      second key   third key
Sign up to request clarification or add additional context in comments.

3 Comments

Pretty cool. Just for the record, month sort is GNU specific feature. It may not work in other Unices.
awesome man, I was also able to use this to sort other item formats. Thanks!
@segfaulter09 Please consider accept this answer, if it's helpful.
3
$ cat input.txt | awk -F '|' '{sprintf("date +%%s -d \"%s\"", $3) | getline tm}; {print tm "\t" $0}' | sort | cut -f2-
pea soup|10.00|05 Jan 2012
peas|10.00|05 Apr 2012
ham|10.00|06 Apr 2012

If you don't want call external command date,
you can write a custom mktime2 function in awk:

#!/bin/gawk -f
# script.awk

BEGIN {
    FS="|"
    m["Jan"] = "01"
    m["Feb"] = "02"
    m["Mar"] = "03"
    m["Apr"] = "04"
    m["May"] = "05"
    m["Jun"] = "06"
    m["Jul"] = "07"
    m["Aug"] = "08"
    m["Sep"] = "09"
    m["Oct"] = "10"
    m["Nov"] = "11"
    m["Dec"] = "12"
}

{
    print mktime2($3) "\t" $0 | "sort | cut -f2-"
}

function mktime2(s,    arr,yyyy,mm,dd)
{
    split(s, arr, " ")
    yyyy = arr[3]
    mm = m[arr[2]]
    dd = arr[1]
    return mktime(sprintf("%s %s %s 00 00 00", yyyy, mm, dd))
}

# make script executable
$ chmod +x script.awk

# run the script
$ ./script.awk input.txt
pea soup|10.00|05 Jan 2012
peas|10.00|05 Apr 2012
ham|10.00|06 Apr 2012

Comments

1

This may work for you (GNU sort):

 sort -t'|' -k3.8,3.11nr -k3.4,3.6Mr -k3.1,3.2nr file

Or (if you don't have GNU sort):

sed '1{x;s/^/Jan01Feb02Mar03Apr04May05Jun06Jul07Aug08Sep09Oct10Nov11Dec12/;x};G;s/\(.*|\(..\) \(...\) \(....\)\)\n.*\3\(..\).*/\4\5\2 \1/' file
sort -nr |
sed 's/^[^ ]* //'

Comments

0

Similar to kev's answer, here's one that doesn't use awk

while IFS=\| read -r item price date ; do printf '%s|%s|%s|%s\n' "$(date +%s -d "$date")" "$item" "$price" "$date" ; done < table.txt | sort -n -t\| | cut -d\| -f2-

The idea is to add a field sort can use, sort by it, then strip it.

1 Comment

this technique is called a Schwartzian Transform

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.