如何使用 GNU "sort" 对一个键进行随机排序,而另一个键保持其原始排序顺序

How to randomly sort one key while the other is kept in its original sort order with GNU "sort"

给出如下输入列表:

405:alice@level1
405:bob@level2
405:chuck@level1
405:don@level3
405:eric@level1
405:francis@level1
004:ac@jjj
004:la@jjj
004:za@zzz
101:amy@floor1
101:brian@floor3
101:christian@floor1
101:devon@floor1
101:eunuch@floor2
101:frank@floor3
005:artie@le2
005:bono@nuk1
005:bozo@nor2

(如您所见,第一个字段是随机排序的(原始输入的所有第一个字段都按数字顺序排列,首先是 004,然后是 005、101、405 等)但是第二个字段在第一个字符上按字母顺序排列。)

我们需要的是随机排序,其中第一个字段(由冒号“:”分隔)是随机排序的,这样第二个字段的所有条目在随机排序期间都无关紧要,只要第一个字段相同的所有行被组合在一起,但随机分布在整个文件中 - 也将第二个字段随机排序。也就是说,在最终输出中,第一个字段中具有相同值的行被组合在一起(但随机分布在整个文件中),但第二个字段也随机排序。我无法获得所需的结果,因为我不太熟悉排序键和诸如此类的东西。

所需的输出类似于:

405:francis@level1
405:don@level3
405:eric@level1
405:bob@level2
405:alice@level1
405:chuck@level1
004:za@zzz
004:ac@jjj
004:la@jjj
101:christian@floor1
101:amy@floor1
101:frank@floor3
101:eunuch@floor2
101:brian@floor3
101:devon@floor1
005:bono@nuk1
005:artie@le2
005:bozo@nor2

有人知道如何实现这种排序吗?

谢谢!

您可以使用 awk 轻松做到这一点。

单线:

awk -F: 'BEGIN{cmd="sort -R"}  != key {close(cmd)} {key=; print | cmd}' input.txt

或者,为了便于解释,拆分开来:

  • -F: - 将 awk 的字段分隔符设置为冒号。
  • BEGIN{cmd="sort -R"} - 在我们开始之前,设置一个变量,它是执行 "randomized sort" 的命令。这个在 FreeBSD 上对我有用。应该也适用于 GNU 排序。
  • != key {close(cmd)} - 如果当前行的第一个字段与最后处理的字段不同,则关闭输出管道...
  • {key=; print | cmd} - 最后,设置 "key" var,并打印当前行,通过存储在 cmd 变量中的命令管道输出。

这种用法利用了一点 awk 的优势。当您通过一个字符串进行管道传输时(无论是否存储在变量中),该管道都会在使用时自动创建。您可以随时关闭它,后续使用将重新打开一个新命令。

这样做的影响是,每次 close(cmd) 时,您都会打印 当前 组随机排序的行。一旦到达文件末尾,awk 会自动关闭 cmd

当然,要使此解决方案起作用,将具有共享第一个字段的所有行组合在一起至关重要。

不是那么优雅,而是一种不同的方法

$ awk -F: '!( in a){a[]=c++} {print a[] "\t" [=10=]}' file | 
  sort -R -k2  | 
  sort -nk1,1 -s | 
  cut -f2-

或者,这个不假设初始分组的备选方案

$ sort -R file | 
  awk -F: '!( in a){a[]=c++} {print a[] "\t" [=11=]}' |
  sort -nk1,1 -s | 
  cut -f2-