Hur kan jag sortera i ett awk-skript på Linux?

Jag har fil fruit som har följande innehåll:

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

Jag skulle vilja sortera filens numeriska data:

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

För att köra awk-skriptet kör jag kommandot :

awk -f numbers fruit 

Nummerfilen har samma innehåll som frukt men dess första och andra fält kopieras till nummerfilen.

Kommentarer

  • Varför behöver du sortera i awk? Awk har inte ’ inte inbyggda sorteringsfunktioner, varför har du inte ’ t du bara sorterar utdata istället?
  • @ terdon GNU awk (som jag TROR är standard awk på Linux) har inbyggda sorteringsfunktioner.
  • @EdMorton se deras sista fråga för något sammanhang. Och du ’ har rätt! GNU awk har asort. Jag kunde ha svurit att det inte ’ t, av någon anledning. Tack! Inte helt säker på om det skulle vara värt det eftersom du ’ d måste läsa hela filen i en matris och sedan sortera matrisen, så sortering av utdata skulle sannolikt fortfarande vara effektivare, men det ’ är mer än tillräckligt för detta.
  • @terdon Det har inte ’ t har bara asort() det har också det mycket mer användbara sorted_in, så att du enkelt kan definiera en ordning för for (i in array) besöka arrayelementen – se gnu.org/software/gawk/manual/gawk.html#Controlling-Scanning.I håller med om att bara piping till UNIX-sort skulle vara effektivare för detta problem.
  • @EdMorton GNU awk är INTE standard i debian och lika. ” mawk ” är standard, vilket inte ’ t har någon ” asort ” inbyggd funktion.

Svar

GNU awk ger dig ett snyggt sätt att kontrollera hur du korsar en matris: se Kontrollera Array Traversal och Kontrollera skanning

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 

utgångar

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 

Svar

Du kan faktiskt skicka awk ”s print till "sort" (notera citaten):

$ 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 

Så, för att skriva till numbers kan du göra:

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

Observera att jag förenklade din awk lite. Det finns inget behov av att använda printf här eller att uttryckligen skriva ut OFS eftersom du inte ändrar det någonstans. så se inte vad din for(i=1;i<=NF;i++)j+=$i gör. Du har redan numret med NR och din printf använde ändå inte j.

Kommentarer

  • Istället för att ringa sort inuti awk är det ’ enklare och mer effektivt att helt enkelt skriva ut i awk och rör awk-utgången för att sortera: awk '{print ...}' fruit | sort ....
  • @EdMorton åh, absolut! Jag skulle aldrig använda detta tillvägagångssätt själv, vad ’ är poängen? Men det är vad OP begärde .
  • Jag hittar ofta ett krav att sortera inom gawk, när jag inte ’ inte vill sortera hela utdata. Till exempel samla in och rapportera statistik separat för varje inmatningsfil. Jag kan använda en dekorera / sortera / klippmetod för att skräddarsy enkla nycklar från komplexa data (t.ex. rangordna överbelastning av elektrisk utrustning med hjälp av en sidmatris med max betyg). Extern sortering använder också diskarbetsfiler och en split / merge-strategi sortera kan använda bättre metoder.
  • @JoeSkora du behöver inte ’ du behöver inte skapa en subshell från awk och hoppas sedan att buffringen från alla berörda leder till utdata från subshell kommer till stdout efter resten av utdata från kommandot awk istället för före det eller, om tillämpligt, mitt i det. Gör bara awk '{print (NR>1), $0}' | sort -k1,1n -k2 | cut -d' ' -f2-
  • @EdMorton Jag gillar att skriva ut den villkorliga, fantastiska idén. Den sista delen kan förenklas genom att ytterligare lämna detta. awk '{print (NR>1),$0}' | sort ... | cut -c3-.

Svar

Jag måste ha haft allvar problem med SunOS nawk 2002. Jag hittade mitt testskript som innehöll tre awk-implementeringar som körs i icke-GNU-awk:

(a) eSort: använder en arbetsfil och läser tillbaka genom ett pipkörningssorteringskommando. Inte bra i mitt fall, för jag gjorde saker genom ssh för agentlös övervakning, och externa arbetsfiler var för invasiva för våra live-servrar.

(b) qSort: en rekursiv partitionsort. Prestanda dålig för stora data och bryter stacken i mawk för> 2000 element. Kul att skriva men.

(c) hSort: en sorterings-in-situ-algoritm på 15 rader. Denna hög använder en indexeringsalgoritm för att stödja ett binärt träd (se Wikipedia).

Detta bash-skript innehåller awk-funktioner hSort och hUp som implementerar den faktiska sorteringen. En åtgärdslinje placerar all inmatning i en matris och END-blocket anropar hSort och rapporterar resultaten.

Ingångsdata är innehållet i ”man bash”, en gång som rader och igen som ord. Vi använder wc för att bevisa att inget har gått vilse, och sortera -c för att bevisa att produktionen sorteras. Tiderna inkluderar läsa och skriva ut overhead.

Detta är testbilden:

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 

Detta är skriptet. Njut av!

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

Svar

HeapSort kan skrivas i standard awk på mindre än 20 rader. Inte bländande snabbt, men det passar språket ganska bra.

Kommentarer

  • Åh, jag gjorde inte ’ inte lägga upp det. Jag hävdade dess existens och lämnade den som en övning för läsaren.
  • Skickade koden och testade den 9 januari 2020

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *