按十六进制值排序


14

使用coreutils sort,如何按十六进制值(字段)对数字进行排序?我期待着一些类似的事情

sort -k3,3x file_to_sort

但是,这样的x不存在。

编辑:到目前为止,我想出的最佳解决方案是:

{ echo ibase=16; cut -d' ' -f3 file_to_sort; } |
  bc | paste -d: - file_to_sort | sort -t: -k1,1n | cut -d: -f2-

其中cut -d' ' -f3隔离搜索字段(这是-k3,3-这可能会有所不同,当然),和bc不转换为十进制(需要大写十六进制,没有0x前缀,匹配我的情况)。然后,我加入,排序和拆分列。


-k3,3?您有盯着0x且长度相同的十六进制数字吗?大写/小写都没有?如果是,则它们在解释为字符串时应该排序正确。也许您可以向我们展示一些示例数据?

@yeti:不幸的是,没有。
stefan 2014年

Answers:


5

解决方案 perl

$ perl -anle '
    push @h, [$F[-1],$_];
    END {
        print for map  { $_->[0] }
                  sort { $a->[1] <=> $b->[1] }
                  map  { [$_->[1],hex($_->[0])] } @h;
    }
' file
4 jjk 7
5 hhf 25
2 ukr 9f
3 ezh ae
1 hdh d12

说明

  • 在处理文件时,我们创建一个数组array @h,其每个元素都是一个数组引用[$F[-1],$_],第一个元素是要比较的十六进制值,第二个元素是整行。

  • END块中,我们使用 Schwartzian变换

    • 使用的每个元素@h,创建一个匿名数组,包含整行($_->[1]每个数组ref中的第二个元素@h)和要比较的十六进制值hex($_->[0])]

    • 根据十六进制值在数组上方排序 $a->[1] <=> $b->[1]

    • 获取排序数组中每个数组ref的第一个元素,map { $_->[0] } 然后打印结果。

更新资料

有了@Joseph R的建议,没有使用Schwartzian转换:

$ perl -anle '
    push @h, [hex($F[-1]),$_];
    END {
        print $_->[1] for
            sort { $a->[0] <=> $b->[0] } @h;
    }
' file

更新2

阅读了斯蒂芬的评论后,我认为可以这样称呼direct

$ perl -e '
    print sort {hex((split(/\s+/,$a))[-1]) <=> hex((split(/\s+/,$b))[-1])} <>;
' file
4 jjk 7
5 hhf 25
2 ukr 9f
3 ezh ae
1 hdh d12

+1,但为什么不只是:print for sort { hex $a->[-1] <=> hex $b->[-1] } @h?该hex运营商是很难昂贵足以保证一个的Schwartzian,不是吗?
2014年

@JosephR .:也许,但是Schwartzian更灵活,在任何情况下都可以工作。我认为我们可以通过在处理时计算十六进制值来获得另一种解决方案,很快就会更新我的答案。
cuonglm 2014年

很酷的解决方案。不知道此模式有一个名称:decorate-sort-undecorate。看到我上面的评论。
stefan 2014年

@stefan:请参阅我的最新答案。
cuonglm 2014年

@Gnouc:是的,您的第二次更新肯定符合直接侵权的资格。我最初的想象。
stefan 2014年

6

我使用以下示例数据:

1 hdh d12
2 ukr 9f
3 ezh ae
4 jjk 7
5 hhf 25

想法是使用十进制形式的排序字段创建此数据的新版本。即awk转换它,将其添加到每一行,对结果进行排序,并在最后一步中删除添加的字段:

awk '{val="0x" $3; sub("^0x0x","0x",val); print strtonum(val),$0 ;}' file | 
  sort -n | 
  sed 's/^[^ ]* //'

结果如下:

4 jjk 7
5 hhf 25
2 ukr 9f
3 ezh ae
1 hdh d12

1
谢谢,很酷的解决方案。抱歉,我没有发布我的编辑,它遵循类似的使用cut + paste的方法。我一直希望有一个更直接的解决方案……
stefan 2014年

@stefan什么算作“直接”?解决方案必须使用sort吗?
2014年

@Joseph“什么算作“直接”?”是正确的问题。到目前为止,基本上所有的解决方案(Hauke's,Gnouc以及我的以下解决方案)都做类似的事情:解码十六进制值,将结果附加到行上,对其进行排序,然后将其删除。我正在寻找不使用decorate-sort-undecorate模式的东西。这两种解决方案都比我的解决方案优越,因为可以在管道中进行。我之所以选择这一工具,是因为我个人更愿意使用awk(较小的锤子)而不是Perl来完成此类任务。
stefan 2014年

由于Gnouc的第二次更新,我已将答案的选择移至下面的#3。
stefan 2014年

1

输入值

$ cat /tmp/input
0x45 aaa 333
0x50 dd 33
0x4 bbbb 444
0x456 cc 22
0x5 eee 1111

排序一个班轮

$ gawk  --non-decimal-data '{ dec = sprintf("%d", $1); print dec " "  $0 }' /tmp/input | sort -n -k 1 | cut -f2- -d' '
0x4 bbbb 444
0x5 eee 1111
0x45 aaa 333
0x50 dd 33
0x456 cc 22

逐步排序

步骤1:添加一个新的第一列,用十六进制数字的十进制表示。

$ gawk  --non-decimal-data '{ dec = sprintf("%d", $1); print dec " "  $0 }' /tmp/input 
69 0x45 aaa 333
80 0x50 dd 33
4 0x4 bbbb 444
1110 0x456 cc 22
5 0x5 eee 1111

步骤2:在第一个字段上对行进行数字排序。

$ gawk  --non-decimal-data '{ dec = sprintf("%d", $1); print dec " "  $0 }' /tmp/input | sort -n -k 1
4 0x4 bbbb 444
5 0x5 eee 1111
69 0x45 aaa 333
80 0x50 dd 33
1110 0x456 cc 22

步骤3:删除第一列。

$ gawk  --non-decimal-data '{ dec = sprintf("%d", $1); print dec " "  $0 }' /tmp/input | sort -n -k 1 | cut -f2- -d' '
0x4 bbbb 444
0x5 eee 1111
0x45 aaa 333
0x50 dd 33
0x456 cc 22

0

改编自:http : //www.unix.com/302548935-post6.html?s=b4b6b3ed50b6831717f6429113302ad6

:文件排序:

6F993B
954B29
A23F2F
BFA91D
C68C15
8F322F
5A6D40
6D512C
9D9D63
B4B823
A0641C
A79716
A18518

命令:

awk '{printf("%050s\t%s\n", toupper($0), $0)}' file-to-sort | LC_COLLATE=C sort -k1,1 | cut -f2

输出:

C68C15
BFA91D
B4B823
A79716
A23F2F
A18518
A0641C
9D9D63
954B29
8F322F
6F993B
6D512C
5A6D40

-其中toupper($ 0)“升级”小写字母,因此它们将首先排序(虽然不确定是否有必要?)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.