ファイルの終わりに達するまでデータブロックを再フォーマットする方法は?

ファイルの終わりに達するまでデータブロックを再フォーマットする方法は?

次のファイルがあります。

# Time-averaged data for fix avetimeall
# TimeStep Number-of-rows
# Row c_gyrationchunkall
1000 3
1 2.09024e-14
2 4.88628
3 5.69321
2000 3
1 2.10518e-14
2 8.33702
3 8.83162
3000 3
1 1.96656e-14
2 12.1396
3 11.5835
...

私のファイルでは、最初の3行は常にヘッダです。ヘッダーの後には、私のファイルに同じサイズのチャンクがリストされており、それぞれはタグサブヘッダーで始まります。各チャンクのデータがそのチャンクラベルの関連部分で始まる行に転送されるようにファイル内のデータを再構成し、そのチャンクの関連データ値をすべてスペースで区切ってリストしたいと思います。たとえば、上記の例を次のように変換したいとします。

# Time-averaged data for fix avetimeall
# TimeStep c_gyrationchunkall
1000 2.09024e-14 4.88628 5.69321
2000 2.10518e-14 8.33702 8.83162
3000 1.96656e-14 12.1396 11.5835
...

Bashでこれを行うにはどうすればよいですか? Bashの経験は少しありますが、この問題をすばやく処理するのに十分ではありません。

答え1

3ブロックの行数が異なる可能性があるかどうかに関係なく、awkを使用してください。

$ awk '
    NR == 2 { $3=""; saved=$0; next }
    NR == 3 { $0=saved $3 }
    NR  < 4 { print; next }
    !numLines {
        numLines = $2
        printf "%s%s", $1, OFS
        next
    }
    { printf "%s%s", $2, (--numLines ? OFS : ORS) }
' file
# Time-averaged data for fix avetimeall
# TimeStep c_gyrationchunkall
1000 2.09024e-14 4.88628 5.69321
2000 2.10518e-14 8.33702 8.83162
3000 1.96656e-14 12.1396 11.5835

以下の議論を続けてください。ザビエルGの答え読みやすさスタイルのデフォルト設定に関して、以下はシェルスクリプトと同じスタイルで書かれたawkスクリプトです(シェルスクリプトに含まれているため、外部でも同じように機能します)。しかし、はるかに速く実行されます*シェルスクリプトよりも強力で移植可能です。 :

$ cat ./script_filename
#!/usr/bin/env bash

awk '
    BEGIN {
        # Reformat comments:
        getline first_line
        print first_line
        getline; split($0,line2)
        getline; split($0,line3)
        printf "# %s %s\n", line2[2], line3[3]

        # Reformat data:
        while ( getline > 0 ) {
            timestep=$1; number_of_rows=$2
            printf "%s", timestep
            for ( i=1; i<=number_of_rows; i++ ) {
                getline; row_value=$NF
                printf " %s", row_value
            }
            print ""
        }
    }
'

$ ./script_filename < input
# Time-averaged data for fix avetimeall
# TimeStep c_gyrationchunkall
1000 2.09024e-14 4.88628 5.69321
2000 2.10518e-14 8.33702 8.83162
3000 1.96656e-14 12.1396 11.5835

*以下は、90,000個のOPレコードを含むファイルでbashスクリプトと上記のawkスクリプトを3回目に実行したタイミングの結果です。

$ time ./script_bash < file > /dev/null

real    0m9.425s
user    0m5.062s
sys     0m4.139s

$ time ./script_awk < file > /dev/null

real    0m0.265s
user    0m0.171s
sys     0m0.000s

答え2

使用幸せ(以前のPerl_6)

skipヘッダー行を一時的に忘れるために使用されます。

~$ raku -e 'my @a = lines.skip(3).rotor(4, partial => True).map: *.words; .[0,3,5,7].put for @a;'  file

#OR

~$ raku -e 'my @a = lines.skip(3).batch(4).map: *.words; .[0,3,5,7].put for @a;'  file

上記は、Perlシリーズのプログラミング言語であるRakuで書かれた答えです。つまり、最初の 3 つのヘッダー行を読み込んでlinesping します。ファイルの末尾にある最後の「スピン」を含むskip4行ごとに一緒にrotor編集されます。その間、各/をスペースで区切ってみましょう。batchpartialrotorbatchwords

これらのローター/配置の4行のそれぞれは、余白から分離され、という@署名配列に格納されます@a。最後に(2番目のステートメントで)for各位置を使用して@a繰り返しput、不要な要素を削除するように注意します(インデックス角かっこを介して[0,3,5,7])。

入力例:

# Time-averaged data for fix avetimeall
# TimeStep Number-of-rows
# Row c_gyrationchunkall
1000 3
1 2.09024e-14
2 4.88628
3 5.69321
2000 3
1 2.10518e-14
2 8.33702
3 8.83162
3000 3
1 1.96656e-14
2 12.1396
3 11.5835

出力例:

1000 2.09024e-14 4.88628 5.69321
2000 2.10518e-14 8.33702 8.83162
3000 1.96656e-14 12.1396 11.5835

putヘッダーラインに関しては、2つのステートメントでRakuコードを起動するのは簡単ですput "Time-averaged data...";。ただし、実際には、OPに必要な出力を提供するタスクです。

~$ raku -e 'lines[0].put; .words[0..1, *-1].put for lines[0..1].rotor(2);  \
            my @a = lines.rotor(4, partial => True).map: *.words;          \
            .[0,3,5,7].put for @a;'  file
## Time-averaged data for fix avetimeall
# TimeStep c_gyrationchunkall
1000 2.09024e-14 4.88628 5.69321
2000 2.10518e-14 8.33702 8.83162
3000 1.96656e-14 12.1396 11.5835

https://raku.org

答え3

使用AWK:

$ awk '
    NR==2{sub(/[[:space:]]+[^[:space:]]+$/,"");rec = $0; next}
    NR==3{$0 = rec OFS $NF};
    NR<4;                                
    NR>3{printf "%s", (NR%4==0) ? ((NR==4) ? "" : ORS) $1 : ($1="")$0 }
   END{if (NR)print ""}' 
$ awk '
   NR==2{sub(/[[:space:]]+[^[:space:]]+$/,"");rec = $0; next}
   NR==3{$0 = rec OFS $NF};
   NR<4;
   $NF ~ /^[0-9]+$/{a=$NF;n=NR+a; sub(/[[:space:]]+[^[:space:]]+$/,""); printf "%s", $0; next}                    
   NR<=n{$1 =""; printf "%s", $0((NR==n) ? ORS : "") }'

答え4

質問に記載されている警告に基づいてサンプル入力をq762948ファイルとして使用すると、単純なawkコマンドを使用してこれを実行できます。

$ head -2 q762948 >result.txt
# dump the comments as required
$ tail +4 q762948 | awk '{c=(NR-1)%4} c==0{p=$1;print ""} c>0{p=$2}{printf p"  "}'>>result.txt    
$ cat result.txt

# Time-averaged data for fix avetimeall
# TimeStep Number-of-rows

1000  2.09024e-14  4.88628  5.69321  
2000  2.10518e-14  8.33702  8.83162  
3000  1.96656e-14  12.1396  11.5835

関連情報