各セクションの最初の行の4番目の値に基づいてファイルセクションをソートする方法は?

各セクションの最初の行の4番目の値に基づいてファイルセクションをソートする方法は?

空白行で区切られた複数のセクションを持つファイルがありますが、各セクションの本文を変更せずに各セクションの最初の行の4番目の値に基づいてすべてのセクションを昇順に並べ替えたいと思います。

入力例

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

出力

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

私は前に試しました

sort -n -k4 file

しかし、これはすべてのファイルに対して機能し、これらの部分を破壊します。

答え1

awk、sort、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

それ以外の場合は、GNU awkを使用して配列の配列を処理し、 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

ループの目的は、同じcnt[]値(たとえば7.135)が異なる入力レコードの4番目のフィールドに複数回表示される場合、出力でキー値の入力順序を維持することです。あるいは、入力を読み取るときに文字列連結を介して同じ結果を得ることもできます(ソートにはまだGNU awkを使用)。

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

gawk専用のアプローチの欠点は、ファイル全体をメモリに保存する必要があり、gawkがないシステムに移植できないことです。上部の awk+sort+cut スクリプトは移植性に優れ、「整列」だけでファイル全体を一度に処理する必要があり、要求ページングなどを使用して大容量ファイルを処理するように設計されているため、それより大きいファイルを処理する可能性はほとんどありません。愚かな質問。

答え2

sortライン中心です。ここで「段落」のファイルを読む必要があります。

これは非常に短いGNU awkプログラムを使用して行うことができます。

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

RS変数(レコード区切り文字)を空の文字列に設定すると、空の行で区切られたレコードを含むファイルが読み込まれます。各レコードは配列に格納され、ソートキーで索引付けされます。

魔法のPROCINFO行は、gawkに配列を繰り返すときにインデックスごとに配列を数字でソートするように指示します。

関連情報