如何重新格式化数据块直到到达文件末尾?

如何重新格式化数据块直到到达文件末尾?

我有一个如下所示的文件:

# 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
...

在我的文件中,前三行始终是标题。在标题之后,我的文件列出了相同大小的数据块,每个数据块都以标签子标题开头。我想重新组织文件中的数据,以便将每个块中的数据发送到以该块标签的相关部分开始的行中,然后列出该块的相关数据值,所有数据都用空格分隔开。作为示例,我想将上面的示例转换为:

# 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

使用任何 awk,无论3块中的行数是否可以变化:

$ 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 脚本,其编写风格与 shell 脚本相同(并包含在 shell 脚本中,因此它在外部的行为方式相同),但它的运行速度要快几个数量级*比 shell 脚本更健壮和可移植:

$ 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 脚本的第三次运行计时结果:

$ 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

上面是用 Raku(Perl 编程语言家族的成员)编写的答案。简而言之,lines读入、skipping 前 3 个标题行。每 4 行都一起rotor编辑batch,包括partial文件末尾的最终“旋转”。当我们这样做时,让我们将每个rotor/batch分成空格分隔的words

这些转子/批次的 4 行每行都在空白处断开,保存在@名为 的签名数组中@a。最后(在第二个语句中),使用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关于标题行,用两个语句启动 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

相关内容