对行块进行排序

对行块进行排序

我有一个包含 4n 行的文件。这是其中包含 8 行的摘录

6115 8.88443
6116 6.61875
6118 16.5949
6117 19.4129
6116 6.619 
6117 16.5979 
6118 19.4111
6115 8.88433  

我想要做的是对一个块进行排序,其中每个块由基于第一列的 4 行组成。摘录的输出应如下所示。

6115 8.88443
6116 6.61875
6117 19.4129
6118 16.5949
6115 8.88433 
6116 6.619 
6117 16.5979 
6118 19.4111 

答案1

一种选择是使用每 N 行添加一个初始序列号前缀(在您的情况下 N=4)。然后将前缀作为主要排序列输入到 中sort

N=4 的示例:

awk '{print int((NR-1)/4), $0}' file.txt | sort -n -k1,1 -k2,2 | cut -f2- -d' '

答案2

如果这是一次性的,并且您不想学习 python、perl 或 awk,您可以学习 basicsplitsort命令。

首先使用以下选项将文件分成 4 行块-l

split -a 6 -l 4 input_file my_prefix_
for fn in my_prefix_*; do
    sort -n -o $fn $fn
done
cat my_prefix_* > output_file
rm my_prefix_*

sort -n按第一列的数值排序(1234 之前的 999) 。-a 6应该处理 26^6*4 行的文件。my_prefix_应该是您使用的目录所特有的内容。

答案3

你可以用 Perl 来做到这一点:

perl -nle '
   push @a,$_;
   unless($. % 4){
       print join "\n",sort {$a <=> $b} @a; # Sort @a, and print its contents
       @a = (); # Empty @a to start a new block
   }
' your_file

这是如何运作的

  • -n--> 为每个输入行运行代码(并将当前行放入$_
  • -l--> 将换行符附加到任何的输出print
  • -e--> 将以下字符串作为 Perl 代码执行
  • 每一行都附加到数组中@a
  • $.保存当前行号,除非该行号不等于 0 模 4,否则我们将继续工作。如果它与 0 模 4 同余,我们到达了一行,其编号是 4 的倍数(块的末尾),在这种情况下,我们按@a升序对条目进行排序,并打印由 a 连接的排序数组中的条目换行符到标准输出。

答案4

以下是一些“纯粹”的awk解决方案:

如果索引始终是相同的递增整数序列 (6115-6119),如示例数据中所示,则可以使用算法“快捷方式”:

awk '{a[$1]=$0} !(NR%4){for(i=6115;i<6119;print a[i++]);}'

这确实

  • 将所有行添加到数组中a,分布在索引位置 6115-6119
  • 在每 4 行 ( !(NR%4)) 上,循环遍历数组内容以按所需顺序打印。

如果您的数字索引始终是四个相同的索引,但不是递增的整数序列,则必须进行排序:

awk '{a[$1]=$0} !(NR%4){asort(a,b); for(i=1;i<5;print b[i++]);}'

注意:这是 GNU awk 的,其他人可能不支持asort


如果每个四块可以有不同的数字 ID:

awk '{a[$1]=$0} !(NR%4){asort(a); for(i=1;i<5;print a[i++]); delete a}'

注:TIL来自@Gilles self-answer(+2) 这种用法delete还不是 POSIX,但得到普遍支持


正确™使用的版本delete

awk '{a[$1]=$0} !(NR%4){asort(a); for(i=1;i<5;delete a[i++]){print a[i]}}'

没有删除的版本,使用更多内存和维度:

awk '{a[n][$1]=$0} !(NR%4){asort(a[n]); for(i=1;i<5;print a[n][i++]); n++}

相关内容