如何使用 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-
给出如下输入列表:
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-