trier par valeur hexadécimale

14

À l'aide de coreutils sort, comment puis-je trier numériquement par une valeur hexadécimale (champ)? Je m'attendais à quelque chose dans le sens de

sort -k3,3x file_to_sort

cependant, un tel xn'existe pas.

Edit: La meilleure solution que j'ai trouvée jusqu'à présent est:

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

où le cut -d' ' -f3isole le champ de recherche (c'est -k3,3- cela peut varier, bien sûr), et bceffectue la conversion en décimal (nécessite un hex en majuscule, sans 0xpréfixe, correspondant à mon cas). Ensuite, je joins, trie et fractionne des colonnes.

Stefan
la source
-k3,3? Vous avez des nœuds hexagonaux commençant par 0x et tous de la même longueur? Pas de mélange de majuscules / minuscules? Si oui, ils doivent trier correctement lorsqu'ils sont interprétés comme des chaînes. Peut-être pouvez-vous nous montrer quelques exemples de données?
@yeti: Malheureusement, non.
Stefan
Demande de fonctionnalité: lists.gnu.org/archive/html/coreutils/2017-08/msg00035.html
Ciro Santilli 19 改造 中心 法轮功 六四 事件

Réponses:

5

Une solution en 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

Explication

  • Pendant le traitement du fichier, nous créons un tableau de tableau @h, chacun de ses éléments est une référence de tableau [$F[-1],$_], avec le premier élément est la valeur hexadécimale à comparer et le deuxième élément est la ligne entière.

  • En ENDbloc, nous utilisons la transformation de Schwartzian :

    • Avec chaque élément de @h, créez un tableau anonyme, contient la ligne entière ( $_->[1]le deuxième élément de chaque tableau ref in @h) et la valeur hexadécimale à comparerhex($_->[0])]

    • Trier au-dessus de la base du tableau sur la valeur hexadécimale $a->[1] <=> $b->[1]

    • Obtenez le premier élément de chaque référence de tableau dans un tableau trié, map { $_->[0] } puis imprimez le résultat.

Mise à jour

Avec la suggestion de @Joseph R, sans utiliser Schwartzian Transform:

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

Update 2

Après avoir lu le commentaire de Stefan, je pense que cela peut appeler 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
cuonglm
la source
+1 mais pourquoi pas seulement: print for sort { hex $a->[-1] <=> hex $b->[-1] } @h? L' hexopérateur n'est pas assez cher pour justifier un Schwartzian, n'est-ce pas?
Joseph R.
@JosephR .: Peut-être, mais un Schwartzian est plus flexible et fonctionne dans tous les cas. Je pense que nous pouvons avoir une autre solution en calculant la valeur hexadécimale pendant le traitement, mettra à jour ma réponse bientôt.
cuonglm
Solution cool. Je ne savais pas que ce motif avait un nom: décorer-trier-décorer. Voir mon commentaire ci-dessus.
stefan
@stefan: voir ma réponse mise à jour.
cuonglm
@Gnouc: oui, votre 2e mise à jour est définitivement qualifiée d'écriture directe. mon imagination initiale.
stefan
6

J'utilise ces données d'exemple:

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

L'idée est de créer une nouvelle version de ces données avec le champ de tri sous forme décimale. C'est-à-dire awkqu'il le convertit, l'ajoute à chaque ligne, le résultat est trié et, comme dernière étape, le champ ajouté est supprimé:

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

Ce qui se traduit par cette sortie:

4 jjk 7
5 hhf 25
2 ukr 9f
3 ezh ae
1 hdh d12
Hauke ​​Laging
la source
1
Merci, solution plutôt cool. Désolé de ne pas avoir posté mon montage plus tôt, il suit une approche similaire en utilisant couper + coller. J'espérais cependant une solution plus directe ...
Stefan
@stefan Qu'est-ce qui compte comme "direct"? La solution doit-elle être utilisée sort?
Joseph R.
@Joseph «Qu'est-ce qui compte comme« direct »?» Est la bonne question. Fondamentalement, toutes les solutions jusqu'à présent (Hauke, Gnouc ci-dessous et la mienne) font quelque chose de similaire: décodez la valeur hexadécimale, attachez le résultat aux lignes, triez-le et supprimez-le. Je cherchais quelque chose n'utilisant pas le motif décorer-trier-décorer . Les deux solutions sont supérieures aux miennes, dans la mesure où elles fonctionnent dans un pipeline. J'ai choisi celui-ci parce que je préfère personnellement utiliser awk (le plus petit marteau) que Perl pour ce genre de tâche.
stefan
J'ai déplacé mon choix de réponse au n ° 3 ci-dessous, à cause de la deuxième mise à jour de Gnouc.
stefan
1

Contribution

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

Tri d'une doublure

$ 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

Tri étape par étape

Étape 1: Ajoutez une nouvelle première colonne avec la représentation décimale du nombre hexadécimal.

$ 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

Étape 2: Triez les lignes numériquement sur le premier champ.

$ 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

Étape 3: supprimez la première colonne.

$ 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
Arun Saha
la source
0

adapté de: http://www.unix.com/302548935-post6.html?s=b4b6b3ed50b6831717f6429113302ad6

: fichier à trier:

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

Commander:

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

Production:

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

- où le toupper ($ 0) "met à niveau" les lettres minuscules pour qu'elles soient triées en premier (vous n'êtes pas sûr que ce soit nécessaire?)

r_alex_hall
la source