“sort -u”和“sort | uniq”有什么区别?

Ben*_*ird 149 bash sort uniq

我在任何地方看到有人需要获得一个排序的、唯一的列表,他们总是通过管道传输到sort | uniq. 我从未见过有人使用的例子sort -u。为什么不?有什么区别,为什么使用 uniq 比使用 unique 标志进行排序更好?

Chr*_*own 150

sort | uniq之前存在sort -u,并且与更广泛的系统兼容,尽管几乎所有现代系统都支持-u- 它是 POSIX。它主要是回到sort -u不存在的时代(如果他们知道的方式继续有效,人们不会倾向于改变他们的方法,看看ifconfigip采用)。

这两者很可能合并,因为删除文件中的重复项需要排序(至少在标准情况下),并且是排序的一个非常常见的用例。由于能够同时执行两个操作(并且由于它在uniq和之间不需要 IPC 的事实),它在内部也更快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
Run Code Online (Sandbox Code Playgroud)

它也不会掩盖 的返回码sort,这可能很重要(在现代 shell 中,有一些方法可以获得它,例如,bash$PIPESTATUS数组,但这并不总是正确的)。

  • 我倾向于使用`sort | uniq` 因为 10 次中有 9 次,我实际上是在使用“uniq -c”。 (42认同)
  • 请注意,`sort -u` 是大约 1979 年第 7 版 UNIX 的一部分。不支持 `-u` 的 `sort` 版本确实是过时的 - 或者是在没有注意 POSIX 法律标准之前的事实标准的情况下编写的。另请参阅 2010 年的 Stack Overflow [Sort & uniq in Linux shell](http://stackoverflow.com/questions/3382936/sort-uniq-in-linux-shell)。 (7认同)
  • +1 表示“9 次 10 我实际上是通过管道传输到 `uniq -c`”(并且可能再次管道传输到 `sort -nr | head`)。我想知道什么相当于`sort | 当我发现 Vim 有 `:sort u` 命令时,我在 Vim 中使用了 uniq`。TIL `sort -u` 也存在。 (5认同)
  • +1 因为`ip`。现在是 2016 年,这篇文章是 2013 年,但我现在只知道 `ip` 命令。 (3认同)
  • 请注意,使用 `sort -n | 时会有所不同。uniq` 与 `sort -n -u`。例如,尾随和前导空格将被 `sort -n -u` 视为重复,但前者不会!`echo -e '测试 \n 测试' | sort -n -u` 返回`test`,但`echo -e 'test \n test' | 排序 -n | uniq` 返回两行。 (2认同)

Sté*_*las 53

使用 POSIX 兼容sorts 和uniqs(GNUuniq目前在这方面不兼容),不同之处在于sort使用语言环境的整理算法来比较字符串(通常用于strcoll()比较字符串),同时uniq检查字节值标识(通常会使用strcmp()) ¹。

这至少有两个原因。

  • 在某些语言环境中,尤其是在 GNU 系统上,有不同的字符排序相同。例如,在 GNU 系统上的 en_US.UTF-8 语言环境中,所有 ??????????... 字符² 和许多其他字符的排序相同,因为它们的排序顺序没有定义。0123456789 阿拉伯数字的排序与其东阿拉伯印度语数字相同(??????????)。

    对于sort -u, ? 排序与 ? 和0123一样????所以sort -u会只保留其中的一个,而 for uniq(不是 GNUuniq使用strcoll()(除了 with -i)),?不同于? 和 0123 与 ???? 不同,因此uniq会认为所有 4 个都是唯一的。

  • 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
Run Code Online (Sandbox Code Playgroud)

¹ 之前版本的 POSIX 规范引起了混淆,但是通过将LC_COLLATE变量列为影响uniq,这在 2018 版中被删除,并且在上述讨论之后澄清了行为。查看对应的Austin组bug

² 2019年编辑。这些已经被修复,但超过 95% 的 Unicode 代码点在 GNU libc 的 2.30 版中仍然具有未定义的顺序。例如,您可以在较新版本中进行测试


CLF*_*CLF 51

一个区别是它uniq有许多有用的附加选项,例如跳过字段进行比较和计算值的重复次数。sort-u标志仅实现未修饰uniq命令的功能。

  • +1 以抵消反对者,因为“没有办法直接从排序中做到这一点”_does_ 回答这个问题...... (15认同)
  • +0.49 是一个有用的答案,但我会将其表述为“不能将 `sort -u` 的输出传递给 `uniq` 以使用后者的一些有用选项,例如跳过字段进行比较和计算重复次数。” (3认同)

Jer*_*bas 8

我更喜欢使用,sort | uniq因为当我尝试使用-u(eliminate duplicates) 选项删除涉及混合大小写字符串的重复项时,结果并不容易理解。

注意:在运行下面的示例之前,您需要通过执行以下操作来模拟标准的 C 整理序列:

LC_ALL=C
export LC_ALL
Run Code Online (Sandbox Code Playgroud)

例如,如果我想对文件进行排序并删除重复项,同时保持字符串的不同情况不同。

$ cat short      #file to sort
Pear
Pear
apple
pear
Apple

$ sort short     #normal sort (in normal C collating sequence)
Apple            #the lower case words are at the end
Pear
Pear
apple
pear

$ sort -f short  #correctly sorts ignoring the C collating order
Apple            #but duplicates are still there
apple
Pear
Pear
pear

$ sort -fu short #By adding the -u option to remove duplicates it is 
apple            #difficult to ascertain the logic that sort uses to remove
Pear             #duplicates(i.e., why did it remove pear instead of Pear?)
Run Code Online (Sandbox Code Playgroud)

这种混淆是通过不使用-u删除重复项的选项来解决的。使用uniq更可预测。下面首先排序并忽略大小写,然后将其传递给以uniq删除重复项。

$ sort -f short | uniq
Apple
apple
Pear
pear
Run Code Online (Sandbox Code Playgroud)

  • `sort` 的 `-u` 选项输出相等运行的 **first**(参见手册页)。因此,`sort -fu` 选取每个不区分大小写的唯一行的第一次出现。`sort` 用于删除重复项的逻辑是可以预测的。 (3认同)

小智 6

我今天发现的另一个区别是基于分隔符进行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
Run Code Online (Sandbox Code Playgroud)