Come posso ordinare allinterno di uno script awk su Linux?

Ho un file fruit con i seguenti contenuti:

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

Vorrei ordinare i dati numerici del file:

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

Per eseguire lo script awk eseguo il comando :

awk -f numbers fruit 

Il file dei numeri ha lo stesso contenuto della frutta ma il suo primo e secondo campo vengono copiati nel file dei numeri.

Commenti

  • Perché è necessario ordinare in awk? Awk ‘ non ha capacità di ordinamento native, perché non ‘ ordinate semplicemente loutput?
  • @ terdon GNU awk (che penso sia awk predefinito su Linux) ha capacità di ordinamento native.
  • @EdMorton vedi la loro ultima domanda per un po di contesto. E tu ‘ hai ragione! GNU awk ha asort. Avrei giurato che non ‘ t, per qualche motivo. Grazie! Non sono del tutto sicuro se ne varrebbe la pena dato che ‘ avresti dovuto leggere lintero file in un array e quindi ordinare larray, quindi lordinamento delloutput sarebbe probabilmente ancora più efficiente, ma ‘ è più che sufficiente per questo.
  • @terdon Non ‘ ha solo asort() ha anche il molto più utile sorted_in, per consentirti di definire semplicemente un ordine per for (i in array) visita gli elementi dellarray – vedi gnu.org/software/gawk/manual/gawk.html#Controlling-Scanning.I concordo sul fatto che il collegamento con lordinamento UNIX sarebbe tuttavia è più efficiente per questo problema.
  • @EdMorton GNU awk NON è limpostazione predefinita in Debian e simili. ” mawk ” è limpostazione predefinita, che ‘ non ha ” asort ” funzione incorporata.

Risposta

GNU awk ti offre un modo preciso per controllare come attraversi un array: vedi Controllo dellattraversamento dellarray e Controllo della scansione

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 

Risposta

Puoi effettivamente passare awk “s print a "sort" (nota le virgolette):

$ 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 

Quindi, per scrivere a numbers, puoi fare:

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

Tieni presente che ho semplificato un po il tuo awk. Non cè bisogno di usare printf qui o di stampare esplicitamente OFS poiché non lo stai cambiando da nessuna parte. quindi non vedere cosa sta facendo il tuo for(i=1;i<=NF;i++)j+=$i. Hai già il numero con NR e il tuo printf non utilizzava j comunque.

Commenti

  • Invece di chiamare sort allinterno di awk, ‘ è più semplice ed efficiente stampare semplicemente in awk e reindirizza loutput di awk per ordinare: awk '{print ...}' fruit | sort ....
  • @EdMorton oh, assolutamente! Non userei mai questo approccio da solo, cosa ‘ è il punto? Ma questo è ciò che lOP ha chiesto .
  • Spesso trovo un requisito per ordinare allinterno di gawk, quando non ‘ voglio ordinare lintero output. Ad esempio, raccogliendo e riportando le statistiche separatamente per ogni file di input. Posso usare un metodo decora / ordina / ritaglia per creare chiavi semplici da dati complessi (ad esempio classificare i sovraccarichi delle apparecchiature elettriche utilizzando una matrice laterale di valori massimi). Inoltre, lordinamento esterno utilizza file di lavoro del disco e una strategia di divisione / unione. sort può usare metodi migliori.
  • @JoeSkora non ‘ bisogno di generare una subshell da awk e poi sperare che il buffering di tutti gli interessati porti alloutput dalla subshell arrivando a stdout dopo il resto delloutput dal comando awk invece che prima o, se applicabile, nel mezzo di esso. Basta fare awk '{print (NR>1), $0}' | sort -k1,1n -k2 | cut -d' ' -f2-
  • @EdMorton Mi piace stampare il condizionale, ottima idea. Lultima parte può essere ulteriormente semplificata lasciando questa. awk '{print (NR>1),$0}' | sort ... | cut -c3-.

Risposta

Devo aver avuto un serio problema con SunOS nawk nel 2002. Ho trovato il mio script di test che conteneva tre implementazioni awk che girano allinterno di awk non GNU:

(a) eSort: usa un file di lavoro e legge indietro attraverso un pipe che esegue il comando sort. Non va bene nel mio caso, perché stavo facendo cose tramite ssh per il monitoraggio senza agenti, ei file di lavoro esterni erano troppo invasivi per i nostri server live.

(b) qSort: un ordinamento di partizioni ricorsivo. Le prestazioni sono negative per dati di grandi dimensioni e interrompe lo stack in mawk per> 2000 elementi. Divertente scrivere però.

(c) hSort: un algoritmo di ordinamento in situ in 15 righe. Questo heap utilizza un algoritmo di indicizzazione per supportare un albero binario (vedere Wikipedia).

Questo script bash contiene le funzioni awk hSort e hUp che implementano lordinamento effettivo. Una riga di azione inserisce tutto linput in un array e il blocco END chiama hSort e riporta i risultati.

I dati di input sono il contenuto di “man bash”, una volta come righe e di nuovo come parole. Usiamo wc per dimostrare che nulla è andato perso e ordina -c per dimostrare che loutput è ordinato. I tempi includono loverhead di lettura e stampa.

Questo è lo scatto di prova:

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 

Questo è lo script. Buon divertimento!

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

Risposta

HeapSort può essere scritto in awk standard in meno di 20 righe. Non incredibilmente veloce, ma si adatta abbastanza bene alla lingua.

Commenti

  • Oh, non lho fatto ‘ t postalo. Ho affermato la sua esistenza e lho lasciato come esercizio per il lettore.
  • Ho pubblicato il codice e il test il 9 gennaio 2020

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *