0

I have a file of different sections separated by blank lines, I want to sort all section ascendingly depending on the 4th value of the 1st line of each section keeping the body of each section as it is without modification.

example input

PT2 energy =  7.135 eV ( -459.5928710 au) ( 57545.0 cm-1) (R=4)
22u22d00 3->6 : -0.535 (0.286)
22202200 4-->6 : -0.344 (0.119)
222u200d 4->8 :  0.256 (0.065)
222u2d00 4->6 : -0.254 (0.065)
R=4  TDM-form-state 1=   0.2702   -0.2855  -0.5610 TDM= 0.69  f: 0.082

PT2 energy =  7.018 eV ( -459.5971543 au) ( 56605.0 cm-1) (R=5)
22u220d0 3->7 : -0.396 (0.156)
222u2d00 4->6 :  0.352 (0.124)
22220ud0 5-->6,7 :  0.326 (0.106)
2222u0d0 5->7 :  0.303 (0.092)
2222u00d 5->8 :  0.271 (0.073)
222ud020 4,5-->7 :  0.267 (0.071)
22u22d00 3->6 : -0.229 (0.052)
R=5  TDM-form-state 1=   0.0860   -0.1785  -0.5446 TDM= 0.58  f: 0.058

PT2 energy =  6.552 eV ( -459.6143027 au) ( 52841.3 cm-1) (R=6)
222u20d0 4->7 : -0.612 (0.374)
2222ud00 5->6 : -0.499 (0.249)
222udud0 4,5-->6,7 : -0.271 (0.074)
R=6  TDM-form-state 1=  -0.2916   -0.0544  -2.1475 TDM= 2.17  f: 0.754

outout

PT2 energy =  6.552 eV ( -459.6143027 au) ( 52841.3 cm-1) (R=6)
222u20d0 4->7 : -0.612 (0.374)
2222ud00 5->6 : -0.499 (0.249)
222udud0 4,5-->6,7 : -0.271 (0.074)
R=6  TDM-form-state 1=  -0.2916   -0.0544  -2.1475 TDM= 2.17  f: 0.754

PT2 energy =  7.018 eV ( -459.5971543 au) ( 56605.0 cm-1) (R=5)
22u220d0 3->7 : -0.396 (0.156)
222u2d00 4->6 :  0.352 (0.124)
22220ud0 5-->6,7 :  0.326 (0.106)
2222u0d0 5->7 :  0.303 (0.092)
2222u00d 5->8 :  0.271 (0.073)
222ud020 4,5-->7 :  0.267 (0.071)
22u22d00 3->6 : -0.229 (0.052)
R=5  TDM-form-state 1=   0.0860   -0.1785  -0.5446 TDM= 0.58  f: 0.058

PT2 energy =  7.135 eV ( -459.5928710 au) ( 57545.0 cm-1) (R=4)
22u22d00 3->6 : -0.535 (0.286)
22202200 4-->6 : -0.344 (0.119)
222u200d 4->8 :  0.256 (0.065)
222u2d00 4->6 : -0.254 (0.065)
R=4  TDM-form-state 1=   0.2702   -0.2855  -0.5610 TDM= 0.69  f: 0.082

I tried with

sort -n -k4 file

but this operated on all the file and destroy the sections

2
  • You should have included 2 blocks with the same key value in your sample input/output as that's an important edge case to consider and test a potential solution against.
    – Ed Morton
    Commented Feb 5, 2021 at 22:29
  • yep, sure this is exactly the case Commented Feb 5, 2021 at 22:38

2 Answers 2

3

With any awk, sort, and cut:

$ awk -v OFS='\t' '!pNF{val=$4} {print val, NR, $0; pNF=NF} END{if (pNF) print val, NR+1, ""}' file |
    sort -k1,1n -k2,2n |
    cut -f3-
PT2 energy =  6.552 eV ( -459.6143027 au) ( 52841.3 cm-1) (R=6)
222u20d0 4->7 : -0.612 (0.374)
2222ud00 5->6 : -0.499 (0.249)
222udud0 4,5-->6,7 : -0.271 (0.074)
R=6  TDM-form-state 1=  -0.2916   -0.0544  -2.1475 TDM= 2.17  f: 0.754

PT2 energy =  7.018 eV ( -459.5971543 au) ( 56605.0 cm-1) (R=5)
22u220d0 3->7 : -0.396 (0.156)
222u2d00 4->6 :  0.352 (0.124)
22220ud0 5-->6,7 :  0.326 (0.106)
2222u0d0 5->7 :  0.303 (0.092)
2222u00d 5->8 :  0.271 (0.073)
222ud020 4,5-->7 :  0.267 (0.071)
22u22d00 3->6 : -0.229 (0.052)
R=5  TDM-form-state 1=   0.0860   -0.1785  -0.5446 TDM= 0.58  f: 0.058

PT2 energy =  7.135 eV ( -459.5928710 au) ( 57545.0 cm-1) (R=4)
22u22d00 3->6 : -0.535 (0.286)
22202200 4-->6 : -0.344 (0.119)
222u200d 4->8 :  0.256 (0.065)
222u2d00 4->6 : -0.254 (0.065)
R=4  TDM-form-state 1=   0.2702   -0.2855  -0.5610 TDM= 0.69  f: 0.082

Otherwise with just GNU awk for arrays of arrays, and sorted_in:

awk '
    BEGIN { RS=""; ORS="\n\n" }
    { recs[$4][++cnt[$4]] = $0 }
    END {
        PROCINFO["sorted_in"] = "@ind_num_asc"
        for (val in recs) {
            for (i=1; i<=cnt[val]; i++) {
                print recs[val][i]
            }
        }
    }
' file

The loop on cnt[] is so that if/when the same value (e.g. 7.135) appears multiple times in the 4th field for different input records the output will retain the input order for that key value. Alternatively you could get the same result with string concatenation when reading the input, e.g. still using GNU awk for sorted_in:

awk '
    BEGIN { RS=""; ORS="\n\n" }
    { recs[$4] = recs[$4] $0 ORS }
    END {
        PROCINFO["sorted_in"] = "@ind_num_asc"
        for (val in recs) {
            printf "%s", recs[val]
        }
    }
' file

The negative to the gawk-only approaches are that they have to store the whole file in memory and they aren't portable to systems without gawk. The awk+sort+cut script at the top is highly portable and only "sort" has to deal with the whole file at once and it's designed to use demand-paging, etc. to handle large files and so is far less likely to have a problem with a huge file than gawk is.

2

sort is line-oriented. Here you need to read the file in "paragraphs".

This can be done with a very short GNU awk program:

gawk -v RS= -v ORS='\n\n' '
  {section[$4] = $0}
  END {
    PROCINFO["sorted_in"] = "@ind_num_asc"
    for (key in section) print section[key]
  }
' file

Setting the RS variable (the record separator) to an empty string reads the file with blank-line separated records. Each record is stored in an array, indexed by the sort key.

The magic PROCINFO line tells gawk to sort the array numerically by index while traversing it.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .