¿Cómo puedo ordenar dentro de un script awk en Linux?

Tengo un archivo fruit que tiene el siguiente contenido:

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

Me gustaría ordenar los datos numéricos del archivo:

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

Para ejecutar el script awk, ejecuto el comando :

awk -f numbers fruit 

El archivo de números tiene el mismo contenido que la fruta, pero su primer y segundo campo se copian en el archivo de números.

Comentarios

  • ¿Por qué necesita ordenar en awk? Awk no ‘ t tiene capacidades de ordenación nativas, ¿por qué no ‘ no simplemente ordena la salida?
  • @ terdon GNU awk (que CREO que es el awk predeterminado en Linux) tiene capacidades de clasificación nativas.
  • @EdMorton vea su última pregunta para algún contexto. ¡Y tienes ‘ razón! GNU awk tiene asort. Podría haber jurado que no ‘ t, por alguna razón. ¡Gracias! No estoy seguro de si valdría la pena, ya que ‘ tendrías que leer todo el archivo en una matriz y luego ordenar la matriz, por lo que ordenar la salida probablemente sea más eficiente. pero ‘ es más que suficiente para esto.
  • @terdon No ‘ solo tiene asort() también tiene la mucho más útil sorted_in, para permitirle simplemente definir un orden para for (i in array) para visite los elementos de la matriz; consulte gnu.org/software/gawk/manual/gawk.html#Controlling-Scanning. estoy de acuerdo en que solo conectar a UNIX Sin embargo, es más eficiente para este problema.
  • @EdMorton GNU awk NO es el valor predeterminado en Debian y similares. » mawk » es el valor predeterminado, que no ‘ no tiene » asort » función incorporada.

Responder

GNU awk le ofrece una forma ordenada de controlar cómo atraviesa una matriz: consulte Control de recorrido de matriz y Control de escaneo

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 

salidas

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 

Respuesta

De hecho, puede pasar awk «s print a "sort" (observe las comillas):

$ 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 

Entonces, para escribir en numbers, puede hacer:

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

Tenga en cuenta que simplifiqué un poco su awk. No es necesario utilizar printf aquí o imprimir explícitamente OFS ya que no lo vas a cambiar en ninguna parte. I al así que no vea lo que está haciendo su for(i=1;i<=NF;i++)j+=$i. Ya tienes el número con NR y tu printf no estaba «t usando j de todos modos.

Comentarios

  • En lugar de llamar a sort inside awk, ‘ es más simple y más eficiente para simplemente imprimir en awk y canalice la salida de awk para ordenar: awk '{print ...}' fruit | sort ....
  • @EdMorton ¡oh, absolutamente! Yo nunca usaría este enfoque, ¿qué ‘ ¿es el punto? Pero esto es lo que pidió el OP .
  • A menudo encuentro un requisito para ordenar dentro de gawk, cuando no ‘ no quiero ordenar toda la salida. Por ejemplo, recopilar e informar estadísticas por separado para cada archivo de entrada. Puedo usar un método de decorar / ordenar / recortar para personalice claves simples a partir de datos complejos (por ejemplo, clasifique las sobrecargas de equipos eléctricos utilizando una matriz lateral de clasificaciones máximas). Además, la clasificación externa utiliza archivos de trabajo de disco y una estrategia de división / fusión. sort puede usar mejores métodos.
  • @JoeSkora, no ‘ no es necesario generar una subcapa de awk y luego esperar que el almacenamiento en búfer de todos los interesados lleve a la salida desde el subshell llegando a stdout después del resto de la salida del comando awk en lugar de antes o, si corresponde, en el medio. Solo haz awk '{print (NR>1), $0}' | sort -k1,1n -k2 | cut -d' ' -f2-
  • @EdMorton Me gusta imprimir el condicional, gran idea. La última parte se puede simplificar aún más dejando esto. awk '{print (NR>1),$0}' | sort ... | cut -c3-.

Responder

Debo haber tenido un problema con SunOS nawk en 2002. Encontré mi script de prueba que contenía tres implementaciones de awk que se ejecutan dentro de awk que no son de GNU:

(a) eSort: usa un archivo de trabajo y vuelve a leer a través de una tubería que ejecuta el comando sort. No es bueno en mi caso, porque estaba haciendo cosas a través de ssh para el monitoreo sin agentes, y los archivos de trabajo externos eran demasiado invasivos para nuestros servidores en vivo.

(b) qSort: una clasificación de partición recursiva. Rendimiento malo para datos grandes y rompe la pila en mawk para> 2000 elementos. Aunque divertido de escribir.

(c) hSort: un algoritmo de clasificación in situ en 15 líneas. Este montón usa un algoritmo de indexación para soportar un árbol binario (ver Wikipedia).

Este script bash contiene funciones awk hSort y hUp que implementan la ordenación real. Una línea de acción pone toda la entrada en una matriz y el bloque END llama a hSort e informa los resultados.

Los datos de entrada son el contenido de «man bash», una vez como líneas y otra vez como palabras. Usamos wc para demostrar que no se perdió nada, y ordenamos -c para probar que la salida está ordenada. Los tiempos incluyen la sobrecarga de lectura e impresión.

Esta es la toma de prueba:

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 

Esta es la secuencia de comandos. ¡Disfruta!

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

Responder

HeapSort se puede escribir en awk estándar en menos de 20 líneas. No es increíblemente rápido, pero se adapta al lenguaje razonablemente bien.

Comentarios

  • Oh, no ‘ t publicarlo. Afirmé su existencia y lo dejé como un ejercicio para el lector.
  • Publiqué el código y la prueba el 9 de enero de 2020

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *