Jak mohu třídit v rámci awk skriptu na Linuxu?

Mám soubor fruit, který má následující obsah:

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

Chtěl bych seřadit číselná data souboru:

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

Abych spustil awk skript, spustím příkaz :

awk -f numbers fruit 

Soubor čísel má stejný obsah jako ovoce, ale jeho první a druhé pole se zkopíruje do souboru čísel.

Komentáře

  • Proč musíte třídit v awk? Awk ‚ nemá nativní možnosti řazení, proč ‚ prostě radit výstup?
  • @ terdon GNU awk (což si myslím, že je výchozí awk v Linuxu) má nativní možnosti třídění.
  • @EdMorton viz jejich poslední otázku pro nějaký kontext. A ‚ máte úplnou pravdu! GNU awk má asort. Mohl jsem z nějakého důvodu přísahat, že to ‚ t. Dík! Nejste si zcela jisti, zda by to stálo za to, protože ‚ musíte přečíst celý soubor do pole a poté pole seřadit, takže řazení výstupu bude pravděpodobně stále efektivnější, ale ‚ je toho víc než dost.
  • @terdon ‚ prostě nemá asort() má také mnohem užitečnější sorted_in, které vám umožní jednoduše definovat objednávku pro for (i in array) navštivte prvky pole – viz gnu.org/software/gawk/manual/gawk.html#Controlling-Scanning.I souhlasím s tím, že pouhé pipování do UNIXového řazení by bylo efektivnější pro tento problém.
  • @EdMorton GNU awk NENÍ výchozí v debianu a podobně. “ mawk “ je výchozí nastavení, které ‚ nemá žádné “ asort “ integrovaná funkce.

odpověď

GNU awk vám poskytuje elegantní způsob, jak řídit, jak procházíte po poli: viz Ovládání procházení pole a Řízení skenování

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 

výstupy

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 

Odpověď

Ve skutečnosti můžete awk „s print projít "sort" (poznamenejte si uvozovky):

$ 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 

Chcete-li tedy napsat na numbers, můžete udělat:

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

Všimněte si, že jsem váš awk trochu zjednodušil. Není třeba zde používat printf nebo výslovně tisknout OFS protože to nikde neměníte. I al takže nechápejte, co vaše for(i=1;i<=NF;i++)j+=$i dělá. Číslo již s NR a printf stejně nepoužíváte j.

Komentáře

  • Místo volání sort inside awk je ‚ jednodušší a efektivnější jednoduše tisknout v awk a potrubí awk výstup třídit: awk '{print ...}' fruit | sort ....
  • @EdMorton ach, absolutně! Nikdy bych tento přístup sám nepoužil, co ‚ o to jde? Ale o to OP požádal .
  • Často shledávám požadavek na třídění v rámci gawk, když ‚ nechci třídit celý výstup. Například shromažďování a vykazování statistik zvlášť pro každý vstupní soubor. Mohu použít metodu zdobení / třídění / klipu přizpůsobit jednoduché klíče ze složitých dat (např. přetížení elektrických zařízení pomocí postranního pole s maximálním hodnocením). Externí třídění také používá pracovní soubory disku a strategii rozdělení / sloučení. třídění může používat lepší metody.
  • @JoeSkora, ‚ nemusíte vytvářet subshell z awk a pak doufat, že ukládání do vyrovnávací paměti od všech zúčastněných povede k výstupu z subshell dostat do stdout po zbytek výstupu z příkazu awk namísto před ním, nebo, je-li to relevantní, v jeho středu. Stačí udělat awk '{print (NR>1), $0}' | sort -k1,1n -k2 | cut -d' ' -f2-
  • @EdMorton, rád tisknu podmíněný skvělý nápad. Poslední část lze dále zjednodušit a ponechat ji. awk '{print (NR>1),$0}' | sort ... | cut -c3-.

Odpověď

Musela jsem mít vážnou problém s SunOS nawk v roce 2002. Našel jsem svůj testovací skript, který obsahoval tři implementace awk, které běží v rámci non-GNU awk:

(a) eSort: používá pracovní soubor a čte zpět pomocí příkazu řazení kanálu. V mém případě to není dobré, protože jsem dělal věci pomocí ssh pro monitorování bez agentů a externí pracovní soubory byly pro naše živé servery příliš invazivní.

(b) qSort: rekurzivní řazení oddílů. Špatný výkon pro velká data a rozdělí zásobník na mawk pro> 2000 prvků. Zábavné psát.

(c) hSort: algoritmus řazení na místě v 15 řádcích. Tato halda používá indexovací algoritmus k podpoře binárního stromu (viz Wikipedia).

Tento bash skript obsahuje awk funkce hSort a hUp, které implementují skutečné řazení. Jeden akční řádek vloží veškerý vstup do pole a END blok zavolá hSort a ohlásí výsledky.

Vstupní data jsou obsahem „man bash“, jednou jako řádky a znovu jako slova. Pomocí wc dokážeme, že se nic neztratilo, a sort -c k prokázání třídění výstupu. Načasování zahrnuje režii pro čtení a tisk.

Toto je testovací snímek:

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 

Toto je skript. Užijte si to!

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

Odpověď

HeapSort lze zapsat standardním awk za méně než 20 řádků. Ne zaslepeně rychlý, ale jazyku to rozumně vyhovuje.

Komentáře

  • Oh, neudělal jsem ‚ t příspěvek. Prosadil jsem její existenci a nechal ji jako cvičení pro čtenáře.
  • Zveřejnil kód a test 9. ledna 2020

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *