For loop 使用模式匹配按顺序对多个输入文件的行块进行排序

For loop 使用模式匹配按顺序对多个输入文件的行块进行排序,for-loop,awk,pattern-matching,For Loop,Awk,Pattern Matching,我的数据块分布在100个文件中,当重新排序时,会遵循数字顺序。例如,如果我有100个数据块,块1、3、5可能在一个文件中,块2、4、6可能在另一个文件中。我需要创建一个输出文件,所有的块按顺序排列:1,2,3,4,5,6 下面是100个输入文件中2个文件的简化版本。每个区块都以ITEM:TIMESTEP开始,需要按照以下行中的数字进行组织,即1000、2000、3000、4000 输入文件1 项目:时间步 1000 项目:原子数 50 2小时0.40.30.006 10214 2 H 0.5 0

我的数据块分布在100个文件中,当重新排序时,会遵循数字顺序。例如,如果我有100个数据块,块1、3、5可能在一个文件中,块2、4、6可能在另一个文件中。我需要创建一个输出文件,所有的块按顺序排列:1,2,3,4,5,6

下面是100个输入文件中2个文件的简化版本。每个区块都以ITEM:TIMESTEP开始,需要按照以下行中的数字进行组织,即1000、2000、3000、4000

输入文件1

项目:时间步 1000 项目:原子数 50 2小时0.40.30.006 10214 2 H 0.5 0.4 0.002 ……12000行之后。。。 项目:时间步 3000 项目:原子数 50 2 H 2.3 1.4 0.3 10214 2 H 2.5 1.3 0.6 ……12000行之后

输入文件2

项目:时间步 2000 项目:原子数 50 2小时0.40.30.006 10214 2 H 0.5 0.4 0.002 ……12000行之后。。。 项目:时间步 4000 项目:原子数 50 2 H 2.3 1.4 0.3 10214 2 H 2.5 1.3 0.6 ……12000行之后

最终的输出文件如下所示

项目:时间步 1000 …剩余的块。。。 项目:时间步 2000 …剩余的块。。。 项目:时间步 3000 …剩余的块。。。 项目:时间步 4000 …剩余的块

到目前为止,我已经在每个块的开头插入了一个名为identifier的标识符字符串:

awk -v n=12,000 '1; NR%n==0 {print "IDENTIFIER"}' in.txt >> out1.txt
我可以打印每个idenitifier字符串后面的每个块所需的N行,循环遍历多个文件

for i in $(seq 1000 1000 10000); do
  awk 'c&&c--;/IDENTIFIER/{c=12,000}' out${i}.txt >> out-final.txt
done

我使用这种方法专门标识每个块的第二行,因为这些数字可以在块本身中重复。但是,我不知道如何修改第二个命令行,以便当标识符后面的值是序列中的下一个数字时,它只打印到out-final.txt。

我建议另一种方法,首先拆分文件,使每个项目都位于自己的文件中,然后按所需顺序合并回文件。例如,对于给定的两个文件

$ awk '/^ITEM: TIMETEP/{h=$0; next} 
                     h {f="item_"$0; print h > f; h=""} 
                       {print > f}' file1 file2 
将创建四个摘录,可以简单地合并回来

$ cat item_{1..4}000 > merged_items
我会使用perl来实现这一点

cat file{1,2} | perl -0777 -ne '
    @records = split /^(?=ITEM: TIMETEP)/m;
    print join "",
        map  { $_->[1] }
        sort { $a->[0] <=> $b->[0] }
        map  { ($n) = /\n(\d+)\n/; [$n, $_] }
        @records;
'

使用每条记录第2行的记录ID和自该记录开始以来的行号作为每条记录的前缀,对该记录ID和行号进行排序,然后在排序后再次删除它们:

$ cat tst.sh
awk '
    BEGIN { OFS="\t" }
    /^ITEM: TIMETEP/ { head=$0; lineNr=1; next }
    lineNr == 1 { recId=$0; print recId, lineNr, head }
    { print recId, ++lineNr, $0 }
' "$@" |
sort -k1,2n |
cut -f3-

$ ./tst.sh file1 file2
ITEM: TIMETEP
1000
ITEM: NUMBER OF ATOMS
50 2 H 0.4 0.3 0.006
10214 2 H 0.5 0.4 0.002
......#12,000 lines later#...
ITEM: TIMETEP
2000
ITEM: NUMBER OF ATOMS
50 2 H 0.4 0.3 0.006
10214 2 H 0.5 0.4 0.002
......#12,000 lines later#...
ITEM: TIMETEP
3000
ITEM: NUMBER OF ATOMS
50 2 H 2.3 1.4 0.3
10214 2 H 2.5 1.3 0.6
......#12,000 lines later#...
ITEM: TIMETEP
4000
ITEM: NUMBER OF ATOMS
50 2 H 2.3 1.4 0.3
10214 2 H 2.5 1.3 0.6
......#12,000 lines later#...
因为上面唯一一个一次处理所有输入而不是逐行处理的命令是sort,它可以处理大量的大文件,因为sort设计用于分页等。要处理大输入,请参阅。

在重新定义f之前,可以添加closef。
$ cat tst.sh
awk '
    BEGIN { OFS="\t" }
    /^ITEM: TIMETEP/ { head=$0; lineNr=1; next }
    lineNr == 1 { recId=$0; print recId, lineNr, head }
    { print recId, ++lineNr, $0 }
' "$@" |
sort -k1,2n |
cut -f3-

$ ./tst.sh file1 file2
ITEM: TIMETEP
1000
ITEM: NUMBER OF ATOMS
50 2 H 0.4 0.3 0.006
10214 2 H 0.5 0.4 0.002
......#12,000 lines later#...
ITEM: TIMETEP
2000
ITEM: NUMBER OF ATOMS
50 2 H 0.4 0.3 0.006
10214 2 H 0.5 0.4 0.002
......#12,000 lines later#...
ITEM: TIMETEP
3000
ITEM: NUMBER OF ATOMS
50 2 H 2.3 1.4 0.3
10214 2 H 2.5 1.3 0.6
......#12,000 lines later#...
ITEM: TIMETEP
4000
ITEM: NUMBER OF ATOMS
50 2 H 2.3 1.4 0.3
10214 2 H 2.5 1.3 0.6
......#12,000 lines later#...