我正在尝试对多列进行排序。结果并不如预期。
这是我的数据(people.txt):
Simon Strange 62
Pete Brown 37
Mark Brown 46
Stefan Heinz 52
Tony Bedford 50
John Strange 51
Fred Bloggs 22
James Bedford 21
Emily Bedford 18
Ana Villamor 44
Alice Villamor 50
Francis Chepstow 56
以下正确运行:
bash-3.2$ sort -k2 -k3 <people.txt
Emily Bedford 18
James Bedford 21
Tony Bedford 50
Fred Bloggs 22
Pete Brown 37
Mark Brown 46
Francis Chepstow 56
Stefan Heinz 52
John Strange 51
Simon Strange 62
Ana Villamor 44
Alice Villamor 50
但是,以下操作并不按预期进行:
bash-3.2$ sort -k2 -k1 <people.txt
Emily Bedford 18
James Bedford 21
Tony Bedford 50
Fred Bloggs 22
Pete Brown 37
Mark Brown 46
Francis Chepstow 56
Stefan Heinz 52
John Strange 51
Simon Strange 62
Ana Villamor 44
Alice Villamor 50
我试图按姓氏排序,然后按名字排序,但您会发现维拉莫斯的顺序不正确。我希望按姓氏排序,然后当姓氏匹配时,按名字排序。
我似乎不明白这应该如何运作。当然,我可以用另一种方式做到这一点(使用 awk),但我想理解排序。
我在 Mac OS X 上使用标准 Bash shell。
答案1
类似于关键规范-k2
意味着要考虑从 2 到行尾的所有字段。所以Villamor 44
之前就结束了Villamor 50
。由于这两行不相等,因此第一个比较sort -k2 -k1
足以区分这两行,并且-k1
不调用第二个排序键。如果两个维拉莫斯的年龄相同,-k1
就会导致他们按名字排序。
要按单个列排序,请使用-k2,2
作为键规范。这意味着使用从#2 到#2 的字段,即仅第二个字段。
sort -k2 -k3 <people.txt
是多余的:它相当于sort -k2 <people.txt
.要按姓氏、名字、年龄排序,请运行以下命令:
sort -k2,2 -k1,1 <people.txt
或者等效地,sort -k2,2 -k1 <people.txt
因为只有这三个字段并且分隔符是相同的。事实上,您将从 中获得相同的效果sort -k2,2 <people.txt
,因为sort
当行子集中的所有键都相同时,使用整行作为最后的手段。
另请注意,默认字段分隔符是非空白和空白之间的过渡,因此键将包含前导空格(在您的示例中,对于第一行,第一个键将为"Emily"
,但第二个键" Bedford"
为 。添加-b
删除这些空白的选项:
sort -b -k2,2 -k1,1
b
也可以通过在键启动规范末尾添加标志来基于每个键来完成此操作:
sort -k2b,2 -k1,1 <people.txt
但要记住一点:一旦您将一个这样的标志添加到键规范中,全局标志(例如-n
,-r
...)就不再适用于它们,因此最好避免混合每个键标志和全局标志。
答案2
对于 GNUsort
你可以这样做,对于 MacOS 不确定:
sort -k2,2 -k1 <people.txt
更新根据评论。引用自man sort
:
-k, --key=KEYDEF
sort via a key; KEYDEF gives location and type
KEYDEF is F[.C][OPTS][,F[.C][OPTS]] for start and stop position, where
F is a field number and C a character position in the field; both are
origin 1, and the stop position defaults to the line's end.
答案3
你可以这样做
$ sort -k2,2 -k1,1 people.txt
Emily Bedford 18
James Bedford 21
Tony Bedford 50
Fred Bloggs 22
Mark Brown 46
Pete Brown 37
Francis Chepstow 56
Stefan Heinz 52
John Strange 51
Simon Strange 62
Alice Villamor 50
Ana Villamor 44
因此,首先-k2,2
您要按姓氏排序。然后,k1,1
按名字排序。
答案4
使用乐(以前称为 Perl_6)
为可能尝试对 Unicode 进行排序的 U&L 用户添加此答案。 Raku 内置了对 Unicode 的高级支持,这个答案(部分)是为了帮助作者理解 Raku 的排序规则。
使用“一元”比较运算符/块(在顶部注释掉)或包含以下内容的二进制块对一列(姓氏)进行排序leg
“小于/等于/大于” 细绳比较运算符。领带保持“相遇”顺序(即稳定排序):
~$ #`{ raku -e '.put for lines.sort: { .words[1] };' #unary block, OR binary block below: }
~$ raku -e '.put for lines.sort: { $^a.words[1] leg $^b.words[1] };' file
Tony Bedford 50
James Bedford 21
Emily Bedford 18
Fred Bloggs 22
Pete Brown 37
Mark Brown 46
Francis Chepstow 56
Stefan Heinz 52
Simon Strange 62
John Strange 51
Ana Villamor 44
Alice Villamor 50
按两列排序,先姓后名。在顶部(注释掉),给出sort
要排序的一元元素列表。下面的第二个例子:更明确地使用两个leg
字符串比较运算符,||
中间有“短路或”:
~$ #`{ raku -e '.put for lines.sort: { .words.[1], .words.[0] }; #list of unary elements, OR binary blocks below: }
~$ raku -e '.put for lines.sort: {$^a.words[1] leg $^b.words[1] || $^a.words[0] leg $^b.words[0] };' file
Emily Bedford 18
James Bedford 21
Tony Bedford 50
Fred Bloggs 22
Mark Brown 46
Pete Brown 37
Francis Chepstow 56
Stefan Heinz 52
John Strange 51
Simon Strange 62
Alice Villamor 50
Ana Villamor 44
上面的Raku代码满足这个问题的标题:“尝试对两个字段进行排序,第二个然后第一个”。但是,如果一列是数字,您可以使用<=>
而不是leg
按数字排序(<=>
通常称为'飞船'操作员)。下面的例子:
以下对三 (3) 列进行排序:姓氏、反向年龄(最早的在前 - 交换$^b
并$^a
用于反向排序),然后是名字。所以在排序输出Simon Strange 62
之前会出现John Strange 51
。
Raku 有一个改进的cmp
运算符,它尝试检测类型并为您进行智能比较(即与 进行字符串比较leg
和与 进行数字比较<=>
)。在下面的第二个例子中,三 cmp
比较给出与第一个示例完全相同的排序输出:
~$ #`{ raku -e '.put for lines.sort: {$^a.words[1] leg $^b.words[1] || $^b.words[2] <=> $^a.words[2] || $^a.words[0] leg $^b.words[0] };' #OR with cmp operator below: }
~$ raku -e '.put for lines.sort: {$^a.words[1] cmp $^b.words[1] || $^b.words[2] cmp $^a.words[2] || $^a.words[0] cmp $^b.words[0] };' file
Tony Bedford 50
James Bedford 21
Emily Bedford 18
Fred Bloggs 22
Mark Brown 46
Pete Brown 37
Francis Chepstow 56
Stefan Heinz 52
Simon Strange 62
John Strange 51
Alice Villamor 50
Ana Villamor 44
最后,“二进制比较器块”(上面)可以让您精确理解/控制排序机制。但如果您愿意,对三列(上面)进行排序可以简化为以下代码:
~$ raku -e '.put for lines.sort: { ~.words[1], -.words[2], ~.words[0] };' file
Tony Bedford 50
James Bedford 21
Emily Bedford 18
Fred Bloggs 22
Mark Brown 46
Pete Brown 37
Francis Chepstow 56
Stefan Heinz 52
Simon Strange 62
John Strange 51
Alice Villamor 50
Ana Villamor 44
https://perl6advent.wordpress.com/2013/12/23/day-23-unary-sort/
https://docs.raku.org/language/101-basics#Stable_sort
https://docs.raku.org/routine/sort
https://docs.raku.org/routine/cmp
https://perl6advent.wordpress.com/2013/12/23/day-23-unary-sort/
https://raku.org