Bash中使用不同列进行排序时出现问题?

4
我正在处理一个包含3个值的文件,一个ID(如果您感兴趣,它们恰好是蛋白质ID),一个值,然后是另一个值。它是用制表符分隔的,因此看起来像这样:
A2M     0.979569315988908       1
AACS    0.925340159491081       1
AAGAB   0.982296215686199       1
AAK1    0.736903840140103       1
AAMP    0.00589711816127862     0.138868449447202
AARS2   1       1
AARS    3.13300124295614e-05    0.00212792325492566
AARSD1  0.527417792161261       1
AASDH   0.869909252023668       1
AASDHPPT        0.763918221284724       1
AATF    0.691907759125663       1
ABAT    0.989693691462661       1
ABCA1   0.601194017450064       1
ABCA5   1       1
ABCA6   1       1

我对将这些ID按字母顺序排序并提取各种值感兴趣。然而,我注意到根据我提取的内容,sort会以不同的方式对ID进行排序。当我执行以下操作时:
    cut --fields\=1,2 input.txt|sort --key=1

生成的文件是:
A2M     0.979569315988908
AACS    0.925340159491081
AAGAB   0.982296215686199
AAK1    0.736903840140103
AAMP    0.00589711816127862
AARS2   1
AARS    3.13300124295614e-05 
AARSD1  0.527417792161261
AASDH   0.869909252023668
AASDHPPT        0.763918221284724
AATF    0.691907759125663
ABAT    0.989693691462661
ABCA1   0.601194017450064
ABCA5   1
ABCA6   1

但是当我执行时:
cut --fields\=1,3 input.txt|sort --key=1

我明白了。
A2M     1
AACS    1
AAGAB   1
AAK1    1
AAMP    0.138868449447202
AARS    0.00212792325492566
AARS2   1
AARSD1  1
AASDH   1
AASDHPPT        1
AATF    1
ABAT    1
ABCA1   1
ABCA5   1
ABCA6   1

请注意,AARS和AARS2的位置被交换了,这是不应该的,因为我只是根据第一列进行排序。我从来没有见过sort会有这样的行为,而且我已经使用bash有一段时间了。这是一个bug吗,还是我做错了什么?

1
无法在v8.21中使用cut/sort重现此处。我使用1,2和1,3都得到了aars->aars2。 - Marc B
在调用 cut 函数时,你不需要转义(或者使用)等号(=)。 - chepner
这太不可思议了,我已经复现了它,但是自己都不敢相信。 - ojblass
@MarcB 我有sort sort (GNU coreutils) 8.4和cut cut (GNU coreutils) 8.4 @shellter 当我在sort中添加-t="\t"时,我得到了"error sort: multi-character tab `=\t'"的错误提示。 - Josh
3个回答

4
--key=1,1选项告诉sort使用从第一个字段到行末的所有“字段”来对输入进行排序。正如@rici首先观察到的那样,默认情况下,这是一种与语言环境相关的排序方式,在许多语言环境中,空格被忽略用于排序。这似乎就是发生的情况。
如果您只想按蛋白质ID排序,则应该使用以下内容:--key=1,1
cut --fields=1,2 input.txt | sort --key=1,1
cut --fields=1,3 input.txt | sort --key=1,1

@rici 解释了如何通过指定考虑空格的排序顺序来解决问题。


这解决了问题。 - Josh

2
您正在使用区域设置感知排序(默认设置)。在许多区域设置中,空格在排序顺序中被明确忽略;这与您的键从第一个字段延伸到行末的事实相结合(这意味着--key选项是多余的),有效地意味着行按照连接而不是有间隔的字段排序。
这里有一个更长的解释:https://stackoverflow.com/a/27951508/1566221 我的偏好是使用LC_COLLATE=C sort ...进行非区域设置感知排序。(例如,定义alias csort="LC_COLLATE=C sort")。在这种情况下,您也可以只使用-k1,1显式终止排序键。如果您的第一列是唯一的,那么就足够了。

对于具有区域设置感知能力的影响,你做出了很好的观察。你非常正确,选择与C语言环境相符的排序规则也可以解决这个问题。 - John Bollinger

0

我认为排序跳过了制表符... 其结果是AARS0.00212792325492566排在AARS21之前,但AARS21又排在AARS3.13300124295614e-05之前。请参见this question

以下代码应该可以解决问题

cut -f1,2 input.txt | sort -t$'\t'

很遗憾,它并没有,但我认为这种制表符的剥离是导致问题的原因。


这并没有解决问题。cut -f1,2 input.txt | sort -t$'\t' 产生了与 cut -f1,3 input.txt | sort -t$'\t' 不同的输出。 - Josh
选项卡绝对是导致这种情况的原因。 - ojblass

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接