Sort multiple lines based on one line

114 Views Asked by At

I've a file with following lines:

Name: Karl
Born: 2003-07-06 17:21:36.964
State: Germany
Hobby: Videogames

Name: Albert
Born: 2003-07-05 18:16:19.369
State: UK
Hobby: Soccer

Name: Mark
Born: 2003-06-15 16:58:21.201
State: Italy
Hobby: Jogging

I need a shell script that orders from the older to the younger the lines between the blank lines based on the Born field. Based on the example given the right file result should be:

Name: Mark
Born: 2003-06-15 16:58:21.201
State: Italy
Hobby: Jogging

Name: Albert
Born: 2003-07-05 18:16:19.369
State: UK
Hobby: Soccer

Name: Karl
Born: 2003-07-06 17:21:36.964
State: Germany
Hobby: Videogames

I'm unable to do such thing, please no perl or python... I need bash. Ty all in advance for your help. Regards, Dave

3

There are 3 best solutions below

6
Ed Morton On BEST ANSWER

Since you alread have some answers, may as well throw out another one... using GNU awk for PROCINFO["sorted_in"]:

$ awk -v RS= -v ORS='\n\n' -F'\n' '
    { recs[$2] = ( $2 in recs ? recs[$2] ORS : "" ) $0 } 
    END {
        PROCINFO["sorted_in"] = "@ind_str_asc"
        for (i in recs) {
            print recs[i]
        }
    }
' file
Name: Mark
Born: 2003-06-15 16:58:21.201
State: Italy
Hobby: Jogging

Name: Albert
Born: 2003-07-05 18:16:19.369
State: UK
Hobby: Soccer

Name: Karl
Born: 2003-07-06 17:21:36.964
State: Germany
Hobby: Videogames

See Multiple-Line Records for what those RS, ORS, and FS (-F) settings do.

The above reads all of your input into memory before printing it and preserves the relative input order of records that have the same timestamp as each other.

6
xpusostomos On

You're breaking the rules trying to get free coding done. Also Unix shell is not particularly good at this kind of thing, and if there's any way you can obtain the data in a different format, like json (for which there are great tools available) you should do that. But I'll give you a free tip on what might work:

while read NAME
do
read BORN
read STATE
read HOBBY
read
echo "$NAME@$BORN@$STATE@$HOBBY" 
done < mydata.txt | sort -r -t@ -k2,2 | (
IFS=@
while read NAME BORN STATE HOBBY
do
echo $NAME
echo $BORN
echo $STATE
echo $HOBBY
echo
done
)

I haven't tested it, but it gives you an idea.

3
jhnc On
mapfile -d '' data <file

data=${data//$'\r'}
data=${data//$'\n'/$'\r'}
data=${data//$'\r\r'/$'\r\n'}

printf '%s' "$data" |
sort -t$'\r' -k2,2 |
tr '\r' '\n'
  • load data
  • convert records into single lines
  • sort
  • convert back to multiple lines

awk '
    NR==FNR { r[NR]=$0; next }
    { print s r[$1]; s="\n" }
' RS= file RS="\n" <(
    grep ^Born: file |
    cat -n | 
    sort -k3
)
  • extract dates, tag with record number, sort
  • load records into awk
  • load sorted dates, extract tag, print record