Comment puis-je trier dans un script awk sous Linux?

Jai un fichier fruit qui a le contenu suivant:

Apples, 12 Pears, 50 Cheries, 7 Strawberries, 36 Oranges, 2 

Je voudrais trier les données numériques du fichier:

for(i=1;i<=NF;i++)j+=$i;printf "Fruit %d%s, %d\n",NR,OFS,$1,j | sort -k 2 > "numbers"; j=0" 

Pour exécuter le script awk, jexécute la commande :

awk -f numbers fruit 

Le fichier des nombres a le même contenu que les fruits mais ses 1er et 2e champs sont copiés dans le fichier des nombres.

Commentaires

  • Pourquoi avez-vous besoin de trier dans awk? Awk na ‘ t pas de capacités de tri natives, pourquoi ‘ trier simplement la sortie à la place?
  • @ terdon GNU awk (que JE PENSE être le awk par défaut sous Linux) a des capacités de tri natives.
  • @EdMorton voir leur dernière question pour un certain contexte. Et vous ‘ avez tout à fait raison! GNU awk a asort. Jaurais pu jurer quil ne ‘ t, pour une raison quelconque. Merci! Je ne sais pas si cela en vaudrait la peine puisque vous ‘ devez lire le fichier entier dans un tableau, puis trier le tableau, donc le tri de la sortie serait probablement encore plus efficace, mais ‘ est plus que suffisant pour cela.
  • @terdon Il n’a ‘ que asort() il a également le sorted_in beaucoup plus utile, pour vous permettre de définir simplement un ordre pour for (i in array) à visitez les éléments du tableau – voir gnu.org/software/gawk/manual/gawk.html#Controlling-Scanning Je suis daccord pour dire que le simple acheminement vers le tri UNIX serait plus efficace pour ce problème cependant.
  • @EdMorton GNU awk nest PAS la valeur par défaut dans Debian et autres.  » mawk  » est la valeur par défaut, ce qui ‘ na aucun  » asort  » fonction intégrée.

Réponse

GNU awk vous offre un moyen efficace de contrôler la façon dont vous parcourez un tableau: voir Contrôle de la traversée des tableaux et Contrôle de lanalyse

gawk -F", " " {fruit[$1] = $2} END { OFS = FS printf "\nordered by fruit name\n" PROCINFO["sorted_in"] = "@ind_str_asc" for (f in fruit) print f, fruit[f] printf "\nordered by number\n" PROCINFO["sorted_in"] = "@val_num_desc" for (f in fruit) print f, fruit[f] } " fruit 

sorties

ordered by fruit name Apples, 12 Cheries, 7 Oranges, 2 Pears, 50 Strawberries, 36 ordered by number Pears, 50 Strawberries, 36 Apples, 12 Cheries, 7 Oranges, 2 

Réponse

Vous pouvez en fait passer awk « s print à "sort" (notez les guillemets):

$ awk "{print "Fruit",NR, $0 | "sort -k 2 -t, -rn"}" fruit Fruit 2 Pears, 50 Fruit 4 Strawberries, 36 Fruit 1 Apples, 12 Fruit 3 Cheries, 7 Fruit 5 Oranges, 2 

Donc, pour écrire dans numbers, vous pouvez faire:

awk "{print "Fruit",NR, $0 | "sort -k 2 -t, -rn > numbers"}" fruit 

Notez que jai un peu simplifié votre awk. Il nest pas nécessaire dutiliser printf ici ou dimprimer explicitement OFS puisque vous ne le modifiez nulle part. alors ne voyez pas ce que fait votre for(i=1;i<=NF;i++)j+=$i. Vous avez déjà le numéro avec NR et votre printf nétait pas « t en utilisant j de toute façon.

Commentaires

  • Au lieu dappeler sort dans awk, il est ‘ plus simple et plus efficace de simplement imprimer dans awk et dirigez la sortie awk pour trier: awk '{print ...}' fruit | sort ....
  • @EdMorton oh, absolument! Je nutiliserais jamais cette approche moi-même, quoi ‘ est le point? Mais cest ce que lOP a demandé .
  • Je trouve souvent une exigence de tri dans gawk, lorsque je ‘ ne veux pas trier toute la sortie. Par exemple, collecter et rapporter les statistiques séparément pour chaque fichier dentrée. Je peux utiliser une méthode décorer / trier / découper pour personnaliser des clés simples à partir de données complexes (par exemple, classer les surcharges déquipements électriques à laide dun tableau latéral de valeurs maximales). De plus, le tri externe utilise des fichiers de travail sur disque et une stratégie de fractionnement / fusion. sort peut utiliser de meilleures méthodes.
  • @JoeSkora vous navez ‘ pas besoin de générer un sous-shell à partir de awk et espérez ensuite que la mise en mémoire tampon de tous les concernés mène à la sortie du sous-shell à stdout après le reste de la sortie de la commande awk au lieu de lavant ou, le cas échéant, au milieu de celui-ci. Faites simplement awk '{print (NR>1), $0}' | sort -k1,1n -k2 | cut -d' ' -f2-
  • @EdMorton Jaime imprimer la bonne idée conditionnelle. La dernière partie peut être simplifiée davantage en laissant cela. awk '{print (NR>1),$0}' | sort ... | cut -c3-.

Réponse

Jai dû avoir un problème sérieux problème avec SunOS nawk en 2002. Jai trouvé mon script de test qui contenait trois implémentations awk qui sexécutent dans awk non-GNU:

(a) eSort: utilise un fichier de travail et relit via un tube exécutant la commande sort. Pas bien dans mon cas, car je faisais des choses via ssh pour la surveillance sans agent, et les fichiers de travail externes étaient trop invasifs pour nos serveurs en direct.

(b) qSort: un tri de partition récursif. Mauvaises performances pour les données volumineuses et casse la pile dans mawk pour plus de 2000 éléments. Amusant à écrire cependant.

(c) hSort: un algorithme de tri in situ en 15 lignes. Ce tas utilise un algorithme dindexation pour supporter un arbre binaire (voir Wikipedia).

Ce script bash contient les fonctions awk hSort et hUp qui implémentent le tri réel. Une ligne daction met toutes les entrées dans un tableau, et le bloc END appelle hSort et rapporte les résultats.

Les données dentrée sont le contenu de « man bash », une fois sous forme de lignes, puis de nouveau sous forme de mots. Nous utilisons wc pour prouver que rien na été perdu et sort -c pour prouver que la sortie est triée. Les délais incluent les frais généraux de lecture et dimpression.

Voici le plan de test:

Paul--) ./hSort Sorted 5251 elements. real 0m0.120s user 0m0.116s sys 0m0.004s 5251 44463 273728 hSort.raw sort: hSort.raw:2: disorder: 5251 44463 273728 hSort.srt Sorted 44463 elements. real 0m1.336s user 0m1.316s sys 0m0.008s 44463 44463 265333 hSort.raw sort: hSort.raw:3: disorder: Commands 44463 44463 265333 hSort.srt 

Voici le script. Amusez-vous bien!

#! /bin/bash export LC_ALL="C" #### Heapsort algorithm. function hSort { #:: (void) < text local AWK=""" #.. Construct the heap, then unfold it. function hSort (A, Local, n, j, e) { for (j in A) ++n; for (j = int (n / 2); j > 0; --j) hUp( j, A[j], n, A); for (j = n; j > 1; --j) { e = A[j]; A[j] = A[1]; hUp( 1, e, j - 1, A); } return (0 + n); } #.. Given an empty slot and its contents, pull any bigger elements up the tree. function hUp (j, e, n, V, Local, k) { while ((k = j + j) <= n) { if (k + 1 <= n && STX V[k] < STX V[k + 1]) ++k; if (STX e >= STX V[k]) break; V[j] = V[k]; j = k; } V[j] = e; } { U[++nU] = $0; } END { sz = hSort( U); printf ("\nSorted %s elements.\n", sz) | "cat 1>&2"; for (k = 1; k in U; ++k) print U[k]; } """ mawk -f <( printf "%s\n" "${AWK}" ) } #### Test Package Starts Here. function Test { time hSort < hSort.raw > hSort.srt for fn in hSort.{raw,srt}; do wc "${fn}"; LC_ALL="C" sort -c "${fn}"; done } AWK_LINE="{ sub (/^[ \011]+/, ""); print; }" AWK_WORD="{ for (f = 1; f <= NF; ++f) print $(f); }" #xxx : > hSort.raw; Test #.. Edge cases. #xxx echo "Hello" > hSort.raw; Test #xxx { echo "World"; echo "Hello"; } > hSort.raw; Test man bash | col -b | mawk "${AWK_LINE}" > hSort.raw; Test man bash | col -b | mawk "${AWK_WORD}" > hSort.raw; Test 

Answer

HeapSort peut être écrit en awk standard en moins plus de 20 lignes. Pas incroyablement rapide, mais il sadapte assez bien à la langue.

Commentaires

  • Oh, je nai pas ‘ t le poster. Jai affirmé son existence et je lai laissé comme exercice pour le lecteur.
  • Jai publié le code et le test le 9 janvier 2020

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *