这里有两个问题。
区域感知排序
基本上,这里的问题是您正在根据您的“语言环境”进行排序,大概是 en_US.UTF-8(或其他一些 Unicode 语言环境)。理论上,区域感知排序将产生一个排序,这是根据该位置的正常排序规则所期望的排序,而非区域感知排序将根据每个字符的“任意”字符代码进行排序。
例如,在区域设置感知排序中,以大写字母开头的单词通常位于以小写字母开头的相同单词之前(或之后),而非区域设置-aware sort 会将所有以大写字母开头的单词放在任何以小写字母开头的单词之前。此外,在英语语言环境中,您可能会发现以 ä 开头的单词与以 a 开头的单词混合在一起,而在瑞典语言环境中,您会在以 z 开头的单词之后找到它们,因为在瑞典语,ä 是第 28 个字母(它位于 å 和 ö 之前,如果您感兴趣的话)。
为了使所有这些工作,您机器上的语言环境描述需要实际描述每个语言环境中预期的排序顺序,特别是默认语言环境,它应该对应于 您会期望。从这个例子可以看出,有时情况并非如此。事实上,它有时会产生出乎意料的结果。
您的示例中发生的情况是您的语言环境的语言环境描述表明空格不参与排序。它还表明数字在字母之前。现在,考虑数据的一个子集(两个文件合并):
rs10108 114516330
rs1010805 38189142
rs1010863 185432942
rs10108 C
rs1010805 T
rs1010863 T
如果我们完全消除空格,那将是:
rs10108114516330
rs101080538189142
rs1010863185432942
rs10108C
rs1010805T
rs1010863T
如果我们然后按照正常的字母规则排序,首先是数字,我们得到:
rs101080538189142
rs1010805T
rs10108114516330
rs1010863185432942
rs1010863T
rs10108C
或者,把空格放回去:
rs1010805 38189142
rs1010805 T
rs10108 114516330
rs1010863 185432942
rs1010863 T
rs10108 C
这些是排序遵循的规则,结果是第一个字段为rs10108 的两行没有被排序在一起。违反直觉,¿没有?
可能正确的解决方案是告诉为您的发行版构建语言环境文件的人,通常的规则是“没有(可见)在某事之前”,这是我们在学校教过的字母排序规则。换句话说,一个空格(不可见)出现在任何字符之前。或者您可以尝试自己修复排序规则文件。
但实际上,解决方案是告诉sort 在默认情况下进行非区域设置感知排序。我这样做:
export LC_COLLATE=C
在我的 bash 启动文件中。 (C 是与编程语言“C”相对应的语言环境的特殊名称,其中符号按其内部字符代码排序。)您也可以在每次想要排序时输入:
LC_COLLATE=C sort test1.txt
-k 参数的含义
用于排序的-k 参数具有基本语法:
<b>-k</b><i>start</i><b>[</b>,<i>end</i><b>]</b><br>
位置start(以及可选的end)定义了用作排序键的文本范围。如果未指定end,则范围继续到行尾。
仓位最简单的形式就是字段号,比如1,意思是“第一个字段”。但是-k1 什么都不做,因为它的确切意思是“使用从第一个字段到行尾的文本”,这与说“使用整行作为排序键”基本相同,即默认。因此,无论何时您看到 -k1,您都应该知道它没有达到预期的效果。
明确指定结束会更精确:-k1,1 表示排序键是从第一个字段(开始)到第一个字段(结束)的文本,或者换句话说,第一个字段.那会更好,但它不会提供有关如何对具有相同第一个字段的两行进行排序的任何提示。默认情况下,标准的sort 实用程序不是“稳定的”,因此无法预测两个这样的行将按哪个顺序排序。添加更多的二级排序字段通常会更好:
sort -k1,1 -k2,2
这实际上意味着“按第一个字段排序,但如果第一个字段相等,则比较第二个字段。”
字段在空白处被分割(即使空白被忽略以进行排序),因此上述与sort -k1,2的不同之处在于它保证将具有相同值的行放在第一个字段中的连续位置。
附录:为什么语言环境在排序时会忽略空格
不幸的是,sort -k1,1 -k2,2 也可能无法执行您想要的操作,特别是如果您在“C”语言环境中执行此操作,因为 sort 使用的排序字段的历史定义。除非使用 -t 选项指定显式分隔符,否则排序字段以每个空白字符开头,然后是非空白字符。因此,除了第一个字段之外的所有字段都以空格开头。如果它们都以相同的空格开头,那很好,但通常通过显式添加正确数量的空格字符来排列字段。这几乎总是会在第一个字段以外的字段上产生错误的排序。
由于这通常不是我们想要的,sort 提供了一种抑制这种烦人行为的方法:b 排序键标志(排序键标志位于 -k 规范的末尾)。这个标志告诉sort 忽略排序键中的前导空格。此外,您可以将-b 指定为命令行选项,然后再指定任何-k 选项以指定所有排序键应被视为具有b 标志。这表明正确的 sort 调用是:
sort -k1,1 -k2,2b
或
sort -b -k1,1 -k2,2
有些人认为必须一直指定b 很烦人(因为它几乎总是您想要的),并且向用户解释他们为什么必须这样做很复杂。因此,将语言环境定义设置为忽略空格似乎更容易,这肯定会导致前导空格被忽略。该“解决方案”的问题在于它产生的结果至少与 sort 导致的结果在字段定义中包含字段之间的空格一样令人困惑,但由于没有简单的修改语言环境的排序规则的方法。