Hvordan kan jeg sortere i et awk-script på Linux?

Jeg har fil fruit med følgende indhold:

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

Jeg vil gerne sortere de numeriske data for filen:

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

For at køre awk-scriptet kører jeg kommandoen :

awk -f numbers fruit 

Tallfilen har samme indhold som frugt, men dens første og andet felt kopieres til nummerfilen.

Kommentarer

  • Hvorfor skal du sortere i awk? Awk har ‘ t indfødte sorteringsfunktioner, hvorfor don ‘ t bare sorterer du output i stedet for?
  • @ terdon GNU awk (som jeg tror er standard awk på Linux) har indfødte sorteringsfunktioner.
  • @EdMorton se deres sidste spørgsmål til noget sammenhæng. Og du ‘ har ret! GNU awk har asort. Jeg kunne have svoret, at det ikke ‘ t, af en eller anden grund. Tak! Ikke helt sikker på, om det ville være det værd, da du ‘ skulle læse hele filen i en matrix og derefter sortere arrayet, så sortering af output ville sandsynligvis stadig være mere effektiv, men det ‘ er mere end nok til dette.
  • @terdon Det har ikke ‘ t har bare asort() det har også den langt mere nyttige sorted_in, så du simpelthen kan definere en ordre for for (i in array) besøg matrixelementerne – se gnu.org/software/gawk/manual/gawk.html#Controlling-Scanning.I er enig i, at bare rørledning til UNIX-sortering ville være mere effektiv til dette problem.
  • @EdMorton GNU awk er IKKE standard i debian og ens. ” mawk ” er standard, hvilket ikke har ‘ t ” asort ” indbygget funktion.

Svar

GNU awk giver dig en pæn måde at kontrollere, hvordan du krydser en matrix: se Kontrol af Array Traversal og Styring af scanning

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 

output

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 faktisk sende awk “s print gennem "sort" (bemærk citaterne):

$ 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å for at skrive til numbers kan du gøre:

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

Bemærk, at jeg forenklet din awk en smule. Det er ikke nødvendigt at bruge printf her eller til eksplicit at udskrive OFS da du ikke ændrer det nogen steder. Jeg al så se ikke hvad din for(i=1;i<=NF;i++)j+=$i laver. Du har allerede nummeret med NR, og din printf brugte alligevel ikke j.

Kommentarer

  • I stedet for at kalde sort inde i awk er det ‘ enklere og mere effektivt at blot udskrive i awk og rør awk-output for at sortere: awk '{print ...}' fruit | sort ....
  • @EdMorton åh, absolut! Jeg ville aldrig bruge denne tilgang selv, hvad ‘ er pointen? Men dette er hvad OPen bad om .
  • Jeg finder ofte et krav om at sortere inden for gawk, når jeg ikke ‘ ikke vil sortere hele output. For eksempel indsamler og rapporterer statistik separat for hver inputfil. Jeg kan bruge en dekorere / sortere / klip metode til skræddersy enkle nøgler fra komplekse data (f.eks. rangere overbelastning af elektrisk udstyr ved hjælp af en sidematrix med maks. klassifikationer). Ekstern sortering bruger også diskarbejdsfiler og en split / fletningsstrategi. sortering kan bruge bedre metoder.
  • @JoeSkora du behøver ikke ‘ behøver ikke at gyde en subshell fra awk og håber derefter at buffering fra alle berørte fører til output fra subshell kommer til stdout efter resten af output fra awk-kommandoen i stedet for før den eller, hvis relevant, i midten af den. Bare gør awk '{print (NR>1), $0}' | sort -k1,1n -k2 | cut -d' ' -f2-
  • @EdMorton Jeg kan godt lide at udskrive den betingede, gode idé. Den sidste del kan forenkles ved yderligere at forlade dette. awk '{print (NR>1),$0}' | sort ... | cut -c3-.

Svar

Jeg må have haft et seriøst problem med SunOS nawk i 2002. Jeg fandt mit testscript, der indeholdt tre awk-implementeringer, der kører inden for ikke-GNU awk:

(a) eSort: bruger en arbejdsfil og læser tilbage gennem en pipeløbende sorteringskommando. Ikke godt i mit tilfælde, fordi jeg lavede ting gennem ssh til agentløs overvågning, og eksterne arbejdsfiler var for invasive til vores live servere.

(b) qSort: en rekursiv partitionssort. Ydeevne er dårlig for store data og bryder stakken i mawk for> 2000 elementer. Sjovt at skrive dog.

(c) hSort: en sorterings-in-situ algoritme på 15 linjer. Denne bunke bruger en indekseringsalgoritme til at understøtte et binært træ (se Wikipedia).

Dette bash-script indeholder awk-funktioner hSort og hUp, der implementerer den aktuelle slags. Én handlingslinje placerer alle input i en matrix, og END-blokken kalder hSort og rapporterer resultaterne.

Inputdataene er indholdet af “man bash”, en gang som linjer og igen som ord. Vi bruger wc til at bevise, at intet gik tabt, og sorter -c for at bevise, at output er sorteret. Tidsindstillingerne inkluderer læse- og udskrivningsomkostningerne.

Dette er testbillede:

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 

Dette er scriptet. Nyd!

#! /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 skrives i standard awk på mindre mere end 20 linjer. Ikke blændende hurtigt, men det passer sproget rimeligt godt.

Kommentarer

  • Åh, det gjorde jeg ikke ‘ t post det. Jeg hævdede dens eksistens og efterlod den som en øvelse for læseren.
  • Sendte koden og testen den 9. januar 2020

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *