连续排序重复数据但行数不同
我有以下格式的数据:
2
1
A 11.364500 48.395199 40.160500 5
B 35.067699 30.944700 24.155300 4
4
2
A 26.274099 38.533298 32.624298 4
B 36.459000 29.662701 17.806299 5
A 15.850300 28.130899 24.753901 4
A 32.098000 33.665699 20.372499 4
5
3
A 17.501801 44.279202 8.005670 5
B 35.853001 43.095901 17.402901 4
B 1.326100 17.127600 39.600300 4
A 9.837760 41.103199 13.062300 5
B 31.686800 44.997501 16.619499 4
3
4
B 31.274700 8.726580 25.267599 4
A 19.032400 41.384701 19.456301 5
B 19.441900 24.286400 6.961680 4
1
5
B 48.973999 15.508400 5.099710 4
6
6
A 42.586601 21.343800 23.280701 5
B 30.145800 13.256200 30.713800 4
B 29.186001 44.353699 9.057160 4
A 39.311199 27.371201 35.473999 5
B 31.437799 30.415001 37.454399 4
B 48.501999 1.277730 21.900600 4
...
数据由多个重复的数据块组成。每个数据块具有相同的格式如下:
First line = Number of lines of data block
Second line = ID of data block
From third line = Actual data lines (with number of lines designated in the first line)
例如,如果我们看到第一个数据块:
2 (=> This data block has 2 number of lines)
1 (=> This data block's ID is 1 (very first data block))
A 11.364500 48.395199 40.160500 5
B 35.067699 30.944700 24.155300 4
(Third and fourth lines of the data block are actual data.
The integer of the first line, the number of lines for this data block, is 2, so the actual data consist of 2 lines.
All other data blocks follow the same format. )
- 前两行是每个重复数据块的标题。第一行指定数据块的行数,第二行是数据块的ID。
- 然后真实数据从每个数据块的第三行开始。正如我所解释的,“真实数据”的行数在第一行中指定为整数。
- 因此,每个数据块的总行数将为 number_of_lines+2。在这个例子中,数据块 1 的总行数是 4,数据块 2 花费了 6 行......
此格式重复 100,000 次。换句话说,我在单个文本文件中有 100,000 个数据块,就像这个例子一样。我可以提供数据块的总数。
我希望做的是按第四列对每个数据块的“实际数据行”进行排序,并以与原始数据文件相同的格式打印出数据。我希望从上面的示例中实现的目标如下:
2
1
A 11.364500 48.395199 40.160500 5
B 35.067699 30.944700 24.155300 4
4
2
A 26.274099 38.533298 32.624298 4
A 15.850300 28.130899 24.753901 4
A 32.098000 33.665699 20.372499 4
B 36.459000 29.662701 17.806299 5
5
3
B 1.326100 17.127600 39.600300 4
B 35.853001 43.095901 17.402901 4
B 31.686800 44.997501 16.619499 4
A 9.837760 41.103199 13.062300 5
A 17.501801 44.279202 8.005670 5
3
4
B 31.274700 8.726580 25.267599 4
A 19.032400 41.384701 19.456301 5
B 19.441900 24.286400 6.961680 4
1
5
B 48.973999 15.508400 5.099710 4
6
6
B 31.437799 30.415001 37.454399 4
A 39.311199 27.371201 35.473999 5
B 30.145800 13.256200 30.713800 4
A 42.586601 21.343800 23.280701 5
B 48.501999 1.277730 21.900600 4
B 29.186001 44.353699 9.057160 4
...
我知道在 Linux 中使用第 4 列数据进行排序的典型命令,即:
sort -gk4 data.txt > data_4sort.txt
但是,在这种情况下,我需要对每个数据块进行排序。另外,每个数据块没有统一的行数,但每个数据块的数据行数是不同的。
我真的不知道我怎么能做到这一点。我正在考虑使用带有 for 循环和/或 awk、sed、split 等的 shell 脚本和 sort 命令。但是我什至不确定如何使用 for 循环来解决这个问题,或者是否真的需要 for 循环和 shell 脚本来执行这种逐块排序。
回答
使用 awk 准备要排序的文本,然后将其通过管道传递给 1 次调用sort,不会产生子shell,也不会创建临时文件:
$ cat tst.sh
#!/usr/bin/env bash
awk '
BEGIN { OFS="t"; recNr=1 }
NF < pNF { ++recNr }
{
print recNr, NF, NR, $0
pNF = NF
}
' "${@:--}" |
sort -k1,1n -k2,2n -k7,7nr -k3,3n |
cut -f4-
$ ./tst.sh file
2
1
A 11.364500 48.395199 40.160500 5
B 35.067699 30.944700 24.155300 4
4
2
A 26.274099 38.533298 32.624298 4
A 15.850300 28.130899 24.753901 4
A 32.098000 33.665699 20.372499 4
B 36.459000 29.662701 17.806299 5
5
3
B 1.326100 17.127600 39.600300 4
B 35.853001 43.095901 17.402901 4
B 31.686800 44.997501 16.619499 4
A 9.837760 41.103199 13.062300 5
A 17.501801 44.279202 8.005670 5
3
4
B 31.274700 8.726580 25.267599 4
A 19.032400 41.384701 19.456301 5
B 19.441900 24.286400 6.961680 4
1
5
B 48.973999 15.508400 5.099710 4
6
6
B 31.437799 30.415001 37.454399 4
A 39.311199 27.371201 35.473999 5
B 30.145800 13.256200 30.713800 4
A 42.586601 21.343800 23.280701 5
B 48.501999 1.277730 21.900600 4
B 29.186001 44.353699 9.057160 4
这是上述 OPs 输入文件执行速度的第三次运行时间差异:
$ time ./tst.sh file >/dev/null
real 0m0.105s
user 0m0.030s
sys 0m0.091s
和当前的 awk 答案为每个要排序的输入块生成一个子shell:
@IceCreamTucan 的回答:
$ cat tst2.sh
#!/usr/bin/env bash
awk '{
if(NF > 1){
print | "sort -k4,4nr"
}
else{
close("sort -k4,4nr")
print
}
}' "${@:--}"
$ time ./tst2.sh file >/dev/null
real 0m0.405s
user 0m0.120s
sys 0m0.121s
@paxdiablo 的回答:
$ cat tst3.sh
#!/usr/bin/env bash
awk '
function sort() {
close(tmp_file)
system("sort -rnk4 "tmp_file"; rm -rf "tmp_file)
}
BEGIN { tmp_file = "/tmp/tmp_file" }
state == 0 && NF == 1 { print ; next }
state == 0 && NF != 1 { print >tmp_file ; state = 1 ; next }
state == 1 && NF != 1 { print >>tmp_file ; next }
state == 1 && NF == 1 { sort() ; print ; state = 0 ; next }
END { if (state == 1) { sort() } }
' "${@:--}"
$ time ./tst3.sh file >/dev/null
real 0m0.804s
user 0m0.060s
sys 0m0.396s