使用“sort”命令按多列对CSV文件进行排序

120
我是一名有用的助手,可以为您翻译文本。

我有一个类似CSV的文件,我想按列优先级对其进行排序,就像SQL中的"ORDER BY"一样。例如,给定以下行:

3;1;2
1;3;2
1;2;3
2;3;1
2;1;3
3;2;1

如果 "ORDER BY" 是 column2, column1, column3,结果将会是:
2;1;3
3;1;2
1;2;3
3;2;1
1;3;2
2;3;1

我想知道如何使用Unix上的sort命令获得相同的结果。

7
顺便提一下,这是一个ssv文件(分号分隔值):P - John Strood
遗憾的是,在引用发生的现实世界中,sort 是一个不可靠的基础。 - Pavel Vlasov
我投票关闭此问题,因为它应该在UNIX.SE上,并且是https://unix.stackexchange.com/questions/52762/trying-to-sort-on-two-fields-second-then-first的重复。 - Dan Dascalescu
4个回答

197

您需要使用 sort 命令的两个选项:

  • --field-separator(或 -t
  • --key=<start,end>(或 -k),来指定排序键,即要按照哪个范围的列(从开始到结束索引)进行排序。由于您想要按照 3 列进行排序,因此需要 3 次指定 -k,分别为列 2,21,13,3

将它们组合起来,

sort -t ';' -k 2,2 -k 1,1 -k 3,3

请注意,即使转义或引用分隔符,sort也无法处理字段包含分隔符的情况。
另外,请注意:这是一个旧问题,属于UNIX.SE,并且一年后在那里也有人问过

旧答案:根据您的系统版本中的 sort,以下方法也可能有效:

sort --field-separator=';' --key=2,1,3

或者,您可能会遇到“字段规范中的杂字符”。根据排序手册,如果您不指定排序键的结束列,则默认为行的末尾。

12
如果值是数字,那么你可能想考虑使用 -n 选项,它将 "按字符串数值比较" 或使用 -g 选项,它将 "按一般数值比较"。数字值的字符串比较会像 1,10,2,20 这样排序。至少在我使用的 CentOS 上,这些都是 sort 命令可用的选项。你应该查阅手册以确定在你的 sort 版本中正确的选项。 - Adam Porad
8
我收到了 sort: stray character in field spec: invalid field specification ‘2,1,3’ 的信息。意思是在使用排序命令时出现错误,原因是字段规范中有多余字符或者无效的字段规范 '2,1,3'。 - Martin Thoma
7
然而,对我来说,“sort --field-separator=',' -r -k3 -k1 -k2 source.csv > target.csv” 运行成功了。 - Martin Thoma
8
@MartinThoma,很久不见了,我遇到了你的问题,并发现 sort --field-separator=';' --key={2,1,3}。这在来自2016年4月的GNU coreutils 8.4中运行良好。 - mrbolichi
4
@mrbolichi 这个符号表示 --key={2,1,3} 使用了 bash 的花括号扩展。 - kvantour
显示剩余11条评论

38

假设您的unsorted.csv文件中有另一行3;10;3,那么我想您希望得到一个按数字排序的结果:

2;1;3
3;1;2
1;2;3
3;2;1
1;3;2
2;3;1
3;10;3

而不是按字母顺序排序:

2;1;3
3;1;2
3;10;3
1;2;3
3;2;1
1;3;2
2;3;1

要实现这个,你需要使用-n

sort --field-separator=';' -n -k 2,2 -k 1,1 -k 3,3 unsorted.csv
值得一提的是需要使用2,2。如果只使用2,那么sort会取从字段2开头到结尾的字符串。2,2确保仅使用第二个字段。

11
"-k 2" 和 "-k 2,2" 的区别是非常重要的指针!我在第一次阅读手册时忽略了这一点。谢谢。 - usonianhorizon
我在源文件中添加了几行额外的内容,分别是3;10;33:10:53:10;23;10;3,当仅使用-k 2,2时,它似乎按第二列和第三列排序。手册上说:“可以多次指定-k选项,在这种情况下,当早期键相等时,后续键将进行比较。”在我的情况下,早期键(值=10)确实相等,但我没有多次指定-k。我不确定这是否是可靠的行为,或者与我的系统(mac)有关。最终,只要主要排序正确,就无所谓了。 - Davos
哦,我看到还有一个 -s 稳定排序,它忽略相等的键,据说根据 man 的说法更快。 - Davos

26

Charlie的答案在Cygwin(版本2.0,GNU textutils)上对我无效,以下方法可行:

sort -t"," -k2 -k1 -k1

3
Cygwin有一个较旧版本的sort。像往常一样,man手册是您的好帮手。 - Charlie Martin
3
我同意@CharlieMartin的观点,你应该查看你系统上的man手册。在CentOS上,我使用了sort --field-separator=';' -k2 -k1 -k3 test.csv命令。 - Adam Porad

-8

2
很好!但在你的情况下,你可以使用 cat unsorted-file | sort | uniq | head -X 命令 - 其中 X 是你想要输出的前几行的数量。 - Slavik Meltser
@SlavikMe 非常感谢您的评论!但是,您的建议得到了不同的结果... 您的建议获取了完全排序文件中的前X行,而我们想要按“键”获取前X行(即,如果您有一个包含名称的CSV文件,则如果按第2列“姓氏”排序,则您的命令可能只会获取3行以“Allen”作为姓氏,而我们的命令将获取“Allen”,“Brittain”,“Charles”等)。还是非常感谢! - Daniel Iversen
6
你错了。在发表评论之前,我建议您尝试我写的命令。请注意,在sorthead之间的管道顺序中有一个uniq命令,它使得在提取顶部行之前对所有排序行进行去重处理。请不要改变原来的意思,并尽可能让内容更加通俗易懂。 - Slavik Meltser
该解决方案未回答问题,因为它明确要求使用sort命令。 - Tiago Cogumbreiro

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