Como posso classificar em um script awk no Linux?

Tenho o arquivo com o seguinte conteúdo:

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

Eu gostaria de classificar os dados numéricos do arquivo:

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 executar o script awk, eu executo o comando :

awk -f numbers fruit 

O arquivo de números tem o mesmo conteúdo que frutas, mas seu primeiro e segundo campos são copiados para o arquivo de números.

Comentários

  • Por que você precisa classificar no awk? Awk não ‘ não tem recursos de classificação nativos, por que não ‘ você apenas classifica a saída?
  • @ terdon GNU awk (que ACHO é o awk padrão no Linux) tem recursos de classificação nativos.
  • @EdMorton veja sua última pergunta para algum contexto. E você ‘ está certo! GNU awk tem asort. Eu poderia jurar que não ‘ t, por algum motivo. Obrigado! Não tenho certeza se valeria a pena, já que você ‘ teria que ler o arquivo inteiro em uma matriz e, em seguida, classificar a matriz, portanto, classificar a saída provavelmente ainda seria mais eficiente, mas ‘ é mais que suficiente para isso.
  • @terdon Não ‘ tem apenas asort() ele também tem o muito mais útil sorted_in, para permitir que você simplesmente defina um pedido para for (i in array) para visite os elementos da matriz – consulte gnu.org/software/gawk/manual/gawk.html#Controlling-Scanning.I concorda que apenas canalizar para classificação UNIX seria mais eficiente para este problema.
  • @EdMorton GNU awk NÃO é o padrão no debian e similares. ” mawk ” é o padrão, que não ‘ tem qualquer ” asort ” função integrada.

Resposta

GNU awk oferece uma maneira elegante de controlar como você atravessa um array: consulte Controlando o Traversal do Array e Como controlar a verificação

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 

saídas

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 

Resposta

Você pode realmente passar awk “s print a "sort" (observe as citações):

$ 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 

Portanto, para escrever para numbers, você pode fazer:

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

Observe que simplifiquei um pouco o seu awk. Não há necessidade de usar printf aqui ou imprimir explicitamente OFS já que você não o está mudando em lugar nenhum. portanto, não veja o que seu for(i=1;i<=NF;i++)j+=$i está fazendo. Você já tem o número com NR e seu printf não estava “usando j de qualquer maneira.

Comentários

  • Em vez de chamar sort dentro do awk, ele ‘ é mais simples e eficiente de simplesmente imprimir no awk e canalize a saída do awk para classificar: awk '{print ...}' fruit | sort ....
  • @EdMorton oh, absolutamente! Eu nunca usaria essa abordagem, o que ‘ é o ponto? Mas isso é o que o OP pediu .
  • Eu sempre encontro um requisito para classificar no gawk, quando eu não ‘ quero classificar a saída inteira. Por exemplo, coletar e relatar estatísticas separadamente para cada arquivo de entrada. Posso usar um método decorar / classificar / recortar para customizar chaves simples de dados complexos (por exemplo, classificar sobrecargas de equipamentos elétricos usando uma matriz lateral de classificações máximas). Além disso, a classificação externa usa arquivos de trabalho de disco e uma estratégia de divisão / fusão. sort pode usar métodos melhores.
  • @JoeSkora você não ‘ não precisa gerar um subshell do awk e então esperar que o buffer de todos os envolvidos conduza à saída do subshell chegando ao stdout após o resto da saída do comando awk em vez de antes dele ou, se aplicável, no meio dele. Basta fazer awk '{print (NR>1), $0}' | sort -k1,1n -k2 | cut -d' ' -f2-
  • @EdMorton Gosto de imprimir a condicional, ótima ideia. A última parte pode ser simplificada ainda mais deixando isso. awk '{print (NR>1),$0}' | sort ... | cut -c3-.

Resposta

Devo ter tido um sério problema com SunOS nawk em 2002. Descobri meu script de teste que continha três implementações de awk executadas em um awk não GNU:

(a) eSort: usa um arquivo de trabalho e lê de volta por meio de um pipe executando o comando sort. Não é bom no meu caso, porque eu estava fazendo coisas por meio de ssh para monitoramento sem agente e arquivos de trabalho externos eram muito invasivos para nossos servidores ativos.

(b) qSort: um tipo de partição recursiva. Desempenho ruim para dados grandes e quebra a pilha em mawk para> 2.000 elementos. Mas divertido de escrever.

(c) hSort: um algoritmo de ordenação in situ em 15 linhas. Este heap usa um algoritmo de indexação para dar suporte a uma árvore binária (consulte a Wikipedia).

Este script bash contém as funções awk hSort e hUp que implementam a classificação real. Uma linha de ação coloca todas as entradas em um array, e o bloco END chama hSort e relata os resultados.

Os dados de entrada são o conteúdo de “man bash”, uma vez como linhas e novamente como palavras. Usamos wc para provar que nada se perdeu e sort -c para provar que a saída está classificada. Os tempos incluem o overhead de leitura e impressão.

Esta é a foto de teste:

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 

Este é o script. Divirta-se!

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

Resposta

HeapSort pode ser escrito em awk padrão em menos mais de 20 linhas. Não é incrivelmente rápido, mas se encaixa razoavelmente bem no idioma.

Comentários

  • Ah, não ‘ t postar. Afirmei sua existência e deixei-o como um exercício para o leitor.
  • Publiquei o código e o teste em 9 de janeiro de 2020

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *