每当我看到有人需要获取已排序的唯一列表时,他们总是通过管道传输到sort | uniq
。我从未见过有人使用 的例子sort -u
。为什么不呢?有什么区别,为什么使用 uniq 比使用 unique 标志进行排序更好?
答案1
sort | uniq
以前就存在sort -u
,并且与更广泛的系统兼容,尽管几乎所有现代系统都支持-u
——它是 POSIX。这主要是对不存在的日子的倒退sort -u
(如果人们知道的方式继续有效,那么人们不会倾向于改变他们的方法,只需看看与采用ifconfig
相比ip
)。
两者很可能合并,因为删除文件中的重复项需要排序(至少在标准情况下),并且是排序的极其常见的用例。由于能够同时执行这两个操作(并且由于它不需要 IPC(进程间通信) 之间uniq
)sort
。特别是如果文件很大,sort -u
可能会使用更少的中间文件来对数据进行排序。
在我的系统上,我一直得到这样的结果:
$ dd if=/dev/urandom of=/dev/shm/file bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 8.95208 s, 11.7 MB/s
$ time sort -u /dev/shm/file >/dev/null
real 0m0.500s
user 0m0.767s
sys 0m0.167s
$ time sort /dev/shm/file | uniq >/dev/null
real 0m0.772s
user 0m1.137s
sys 0m0.273s
它也不会屏蔽 的返回码sort
,这可能很重要(在现代 shell 中,有多种方法可以获取它,例如bash
的$PIPESTATUS
数组,但这并不总是正确的)。
答案2
对于 POSIX 兼容的sort
s 和uniq
s(GNUuniq
目前在这方面不兼容),有区别其中sort
使用区域设置的整理算法来比较字符串(通常用于strcoll()
比较字符串),同时uniq
检查字节值标识(通常使用strcmp()
)。
这很重要,至少有两个原因。
在某些语言环境中,尤其是在 GNU 系统上,有不同的字符可以进行相同的排序。例如,在 GNU 系统上的 en_US.UTF-8 语言环境中,所有①②③④⑤⑥⑦⑧⑨⑩...字符²和许多其他字符的排序相同,因为它们的排序顺序未定义。 0123456789 阿拉伯数字的排序与其相同东阿拉伯印度语对应物(٠١٢٣٤٥٦٧٨٩)。
对于
sort -u
,① 与 ② 排序相同,0123 与 ٠١٢٣ 排序相同,因此sort -u
仅保留其中一个,而对于(不是使用(除了)的uniq
GNU ),① 与 ② 不同,0123 与 ٠١٢٣ 不同,因此会考虑所有4独特。uniq
strcoll()
-f
uniq
strcoll
只能比较有效字符的字符串(当输入具有不形成有效字符的字节序列时,根据 POSIX 未定义行为),而不strcmp()
关心字符,因为它只进行字节到字节的比较。因此sort -u
,如果其中一些行未形成有效文本,这就是为什么可能无法为您提供所有唯一行的另一个原因。sort|uniq
,虽然在非文本输入上仍未指定,但实际上更有可能因此为您提供独特的行。
除了这些微妙之处之外,到目前为止还没有注意到的一件事是,从uniq
词法上比较整行,而sort
s-u
根据命令行上给出的排序规范进行比较。
$ printf '%s\n' 'a b' 'a c' | sort -uk 1,1
a b
$ printf '%s\n' 'a b' 'a c' | sort -k 1,1 | uniq
a b
a c
$ printf '%s\n' 0 -0 +0 00 '' | sort -n | uniq
0
-0
+0
00
$ printf '%s\n' 0 -0 +0 00 '' | sort -nu
0
1 POSIX 规范的早期版本通过将LC_COLLATE
变量列为影响 的变量而引起混乱uniq
,该变量在 2018 年版本中被删除,并且在上述讨论之后澄清了行为。看相应的奥斯汀组错误
²2019年编辑。这些问题已经得到解决,但是自 GNU libc 版本 2.30 起,超过 95% 的 Unicode 代码点仍然具有未定义的顺序。你可以测试
答案3
一个区别是它uniq
有许多有用的附加选项,例如跳过字段进行比较和计算值的重复次数。sort
的-u
标志仅实现未修饰命令的功能uniq
。
答案4
我今天发现的另一个区别是,当基于分隔符进行排序时,sort -u
仅在您排序的列上应用唯一标志。
$ cat input.csv
3,World,1
1,Hello,1
2,Hello,1
$ cat input.csv | sort -t',' -k2 -u
1,Hello,1
3,World,1
$ cat input.csv | sort -t',' -k2 | uniq
1,Hello,1
2,Hello,1
3,World,1