对具有相同列的两个文件进行排序会给出不同的排序

Sorting two files that have the same column gives different sorting

对不起标题,但我不知道如何解释:

我正在尝试对两个文件进行排序,因为我想合并它们,它们看起来像这样:

test1.txt

rs1010735   224915429
rs1010805   38189142
rs10108     114516330
rs1010863   185432942
rs1010891   110712154
rs1010910   61212213
rs1011124   7533164

test2.txt

rs1010735 C
rs1010805 T
rs1010863 T
rs1010891 T
rs10108  C
rs1010910 A
rs1011124 A

我使用 sort -k1 test1.txtsort -k1 test2.txt 得到了这个:

test1_sort.txt

rs1010735   224915429
rs1010805   38189142
rs10108 114516330
rs1010863   185432942
rs1010891   110712154
rs1010910   61212213
rs1011124   7533164

test2_sort.txt

rs1010735   C
rs1010805   T
rs1010863   T
rs1010891   T
rs10108     C
rs1010910   A
rs1011124   A

如果前两列具有相同的值,为什么会有不同的排序。

我也试过 sort -n -s k1,1 但得到了相同的结果。

添加空格:

$ sort -k 1,1 /tmp/2
rs1010735 C
rs10108  C
rs1010805 T
rs1010863 T
rs1010891 T
rs1010910 A
rs1011124 A
$ sort -k 1,1 /tmp/1
rs1010735   224915429
rs10108     114516330
rs1010805   38189142
rs1010863   185432942
rs1010891   110712154
rs1010910   61212213
rs1011124   7533164

这里有两个问题。

区域感知排序

基本上,这里的问题是您根据您的“语言环境”进行排序,这大概是 en_US.UTF-8(或其他一些 Unicode 语言环境)。理论上,区域感知排序将产生一个排序,这是根据该位置的正常排序规则所预期的顺序,而非区域感知排序将根据每个字符的“任意”字符代码进行排序。

例如,在区域感知排序中,以大写字母开头的单词出现在以小写字母开头的相同单词之前(或之后)是很常见的,而非-locale-aware sort 会将所有以大写字母开头的单词放在以小写字母开头的单词之前。此外,在英语语言环境中,您可能会发现以 ä 开头的单词与以 a 开头的单词混合在一起,而在瑞典语语言环境中,您会在以 z 因为在瑞典语中,ä 是第 28 个字母(如果您感兴趣,它位于 å 之后和 ö 之前)。

为了使所有这些都起作用,您机器上的语言环境描述需要实际描述每个语言环境中预期的排序顺序,尤其是默认语言环境,它应该对应于 期待。从这个例子可以看出,有时情况并非如此。事实上,它有时会产生奇怪的意外结果。

您的示例中发生的情况是,您的语言环境的语言环境描述表明白色space 不参与分类。它还表示数字出现在字母之前。现在,考虑数据的一个子集(两个文件合并):

rs10108     114516330
rs1010805   38189142
rs1010863   185432942
rs10108     C
rs1010805   T
rs1010863   T

如果我们完全消除白色space,那就是:

rs10108114516330
rs101080538189142
rs1010863185432942
rs10108C
rs1010805T
rs1010863T

然后,如果我们按照正常的字母规则对其进行排序,首先是数字,我们将得到:

rs101080538189142
rs1010805T
rs10108114516330
rs1010863185432942
rs1010863T
rs10108C

或者,把白色space放回去:

rs1010805   38189142
rs1010805   T
rs10108     114516330
rs1010863   185432942
rs1010863   T
rs10108     C

这些是排序遵循的规则,结果是第一个字段为 rs10108 的两行没有被排序在一起。违反直觉,不?

可能正确的解决方案是告诉为您的发行版构建区域设置文件的人,正常规则是“无(可见)先于某物”,这是我们在学校教授的字母顺序规则。换句话说,space(不可见)出现在任何字符之前。或者您可以尝试自己修复整理文件。

但实际上,解决方案是让 sort 默认执行非区域感知排序。我通过输入:

export LC_COLLATE=C

在我的 bash 启动文件中。 (C 是编程语言“C”对应的语言环境的特殊名称,其中符号按其内部字符代码排序。)您也可以在每次要排序时键入:

LC_COLLATE=C sort test1.txt

-k 参数的含义

排序的 -k 参数具有基本语法:

<b>-k</b><i>开始</i><b>[</b>,<i>结束</i><b>]</b><br/>

其中位置 start(以及可选的 end)定义了用作排序键的文本范围。如果未指定 end,则范围继续到行尾。

位置最简单的形式就是一个字段号,比如1,意思是“第一个字段”。但是 -k1 什么都不做,因为它的意思是,准确地说,“使用从第一个字段到行尾的文本”,这与说“使用整行作为排序关键字”本质上是一样的,是默认值。因此,只要您看到 -k1,您就应该知道它没有按预期进行。

明确指定结束会更精确:-k1,1 表示排序键是从第一个字段(开始)到第一个字段(结束)的文本,或者换句话说, 第一个字段。那会更好,但它不会提供任何关于如何对具有相同第一个字段的两行进行排序的提示。默认情况下,标准 sort 实用程序不是“稳定的”,因此无法预测这两行的排序顺序。通常添加更多的二级排序字段会更好:

sort -k1,1 -k2,2 

这实际上意味着“按第一个字段排序,但如果第一个字段相等,则比较第二个字段。”

字段在whitespace处分割(即使忽略whitespace进行排序),所以上面与sort -k1,2的不同之处在于保证将行与连续位置的第一个字段中的相同值。


附录:为什么语言环境在排序时忽略白色space

不幸的是,sort -k1,1 -k2,2 也可能不会执行您想要的操作,特别是如果您在“C”语言环境中执行此操作,因为 sort 使用的排序字段的历史定义。除非使用 -t 选项指定明确的定界符,否则排序字段以每个白色 space 字符开始,后面跟着一个非白色 space 字符。因此,除了第一个字段之外的所有字段都以白色space 开头。如果它们都以相同的白色 space 开头,那很好,但通常通过显式添加正确数量的 space 字符来排列字段。这几乎总是在第一个字段以外的字段上产生不正确的排序。

由于这通常不是我们想要的,sort 提供了一种抑制这种恼人行为的方法:b 排序键标志(排序键标志位于 -k 规范)。此标志告诉 sort 忽略排序键中的 leading whitespace。此外,您可以在任何 -k 选项之前将 -b 指定为命令行选项,以指定所有排序键都应被视为具有 b 标志。这表明正确的排序调用是:

sort -k1,1 -k2,2b

sort -b -k1,1 -k2,2

有些人认为必须一直指定 b 很烦人(因为它几乎总是您想要的),并且向用户解释为什么他们必须这样做很复杂。因此,设置语言环境定义以忽略 whitespace 似乎更容易,这肯定会导致前导 whitespace 被忽略。该“解决方案”的问题在于,它产生的结果至少与 sort 在字段定义中包含字段之间的 space 所导致的结果一样令人困惑,但更难进行修复,因为没有简单的方法来修改区域设置的整理顺序。