Hoe kan ik sorteren binnen een awk-script op Linux?

Ik heb bestand fruit met de volgende inhoud:

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

Ik wil de numerieke gegevens van het bestand sorteren:

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

Om het awk-script uit te voeren, voer ik het commando uit :

awk -f numbers fruit 

Het nummerbestand heeft dezelfde inhoud als fruit, maar het 1e en 2e veld worden gekopieerd naar het nummerbestand.

Opmerkingen

  • Waarom moet je in awk sorteren? Awk heeft ‘ geen native sorteermogelijkheden, waarom zou u ‘ niet gewoon de uitvoer sorteren?
  • @ terdon GNU awk (wat volgens mij de standaard awk is onder Linux) heeft native sorteermogelijkheden.
  • @EdMorton zie hun laatste vraag voor een context. En je ‘ hebt helemaal gelijk! GNU awk heeft asort. Ik had kunnen zweren dat het om de een of andere reden niet ‘ t deed. Bedankt! Niet helemaal zeker of het de moeite waard zou zijn, aangezien je ‘ het hele bestand in een array moet lezen en dan de array moet sorteren, dus het sorteren van de uitvoer zou waarschijnlijk nog efficiënter zijn, maar het ‘ is hier meer dan genoeg voor.
  • @terdon Het heeft niet ‘ t heeft alleen asort() het heeft ook de veel nuttiger sorted_in, waarmee u eenvoudig een volgorde kunt definiëren voor for (i in array) tot bezoek de array-elementen – zie gnu.org/software/gawk/manual/gawk.html#Controlling-Scanning.I ben het ermee eens dat gewoon doorsluizen naar UNIX-sortering zou zijn efficiënter voor dit probleem.
  • @EdMorton GNU awk is NIET de standaard in debian en dergelijke. ” mawk ” is de standaard, die ‘ geen ” asort ” ingebouwde functie.

Antwoord

GNU awk geeft je een handige manier om te bepalen hoe je over een array beweegt: zie Array Traversal besturen en Scannen beheren

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 

outputs

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 

Answer

Je kunt awk “s print doorgeven aan "sort" (let op de aanhalingstekens):

$ 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 

Dus om naar numbers te schrijven, kun je het volgende doen:

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

Merk op dat ik je awk een beetje vereenvoudigd heb. Het is niet nodig om printf hier te gebruiken of om expliciet af te drukken OFS aangezien je het nergens wijzigt. dus kijk niet wat uw for(i=1;i<=NF;i++)j+=$i aan het doen is. Je hebt het nummer al met NR en je printf heeft j toch niet gebruikt.

Reacties

  • In plaats van sort binnen awk aan te roepen, is het ‘ eenvoudiger en efficiënter om gewoon af te drukken in awk en pijp de awk-uitvoer om te sorteren: awk '{print ...}' fruit | sort ....
  • @EdMorton oh, absoluut! Ik zou deze benadering zelf nooit gebruiken, wat ‘ is het punt? Maar dit is wat het OP vroeg .
  • Ik vind vaak een vereiste om te sorteren binnen gawk, wanneer ik ‘ niet de hele uitvoer wil sorteren. Bijvoorbeeld statistieken verzamelen en rapporteren afzonderlijk voor elk invoerbestand. Ik kan een decorate / sort / clip-methode gebruiken om maak eenvoudige sleutels op maat van complexe gegevens (bijv. rangschik overbelasting van elektrische apparatuur met behulp van een zijreeks van maximale beoordelingen). Ook gebruikt extern sorteren schijfwerkbestanden en een strategie voor splitsen / samenvoegen. sort kan betere methoden gebruiken.
  • @JoeSkora je hoeft niet ‘ een subshell uit awk te spawnen en dan hopen dat het bufferen van alle betrokkenen leidt tot de uitvoer vanuit de subshell naar stdout gaan na de rest van de uitvoer van het awk-commando in plaats van ervoor of, indien van toepassing, in het midden ervan. Gewoon doen awk '{print (NR>1), $0}' | sort -k1,1n -k2 | cut -d' ' -f2-
  • @EdMorton Ik druk graag het voorwaardelijke, geweldige idee af. Het laatste deel kan verder worden vereenvoudigd. awk '{print (NR>1),$0}' | sort ... | cut -c3-.

Antwoord

Ik moet een serieuze probleem met SunOS nawk in 2002. Ik vond mijn testscript dat drie awk implementaties bevatte die binnen niet-GNU awk draaien:

(a) eSort: gebruikt een werkbestand en leest terug via een pipe met sorteercommando. Niet goed in mijn geval, omdat ik dingen deed via ssh voor agentless monitoring, en externe werkbestanden waren te invasief voor onze live servers.

(b) qSort: een recursieve partitiesortering. Prestaties slecht voor grote gegevens, en breken de stapel in mawk voor> 2000 elementen. Maar leuk om te schrijven.

(c) hSort: een sort-in-situ algoritme in 15 regels. Deze heap gebruikt een indexeringsalgoritme om een binaire boomstructuur te ondersteunen (zie Wikipedia).

Dit bash-script bevat awk-functies hSort en hUp die de daadwerkelijke sortering implementeren. Eén actieregel plaatst alle invoer in een array, en het END-blok roept hSort aan en rapporteert de resultaten.

De invoergegevens zijn de inhoud van “man bash”, eenmaal als regels en opnieuw als woorden. We gebruiken wc om te bewijzen dat er niets verloren is gegaan, en sorteren -c om te bewijzen dat de uitvoer is gesorteerd. De timing is inclusief de lees- en printoverhead.

Dit is de testopname:

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 

Dit is het script. Veel plezier!

#! /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 

Antwoord

HeapSort kan in standaard awk worden geschreven in minder dan 20 regels. Niet verblindend snel, maar het past redelijk goed in de taal.

Reacties

  • Oh, dat deed ik niet ‘ t plaats het. Ik bevestigde het bestaan ervan en liet het achter als een oefening voor de lezer.
  • De code en test gepost op 9 januari 2020

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *