Existe uma maneira mais elegante de contar palavras e atribuir essa contagem a variáveis?

5

Eu tenho um script:

#!/bin/bash

/root/xiotech status > xiostatus.tmp
SyncCount=$(grep -c Sync xiostatus.tmp)
PauseCount=$(grep -c paused xiostatus.tmp)
CopyingCount=$(grep -c Copying xiostatus.tmp)

if [ "$SyncCount" -eq "11" ]
then echo All 11 mirrors are in sync.

else echo $PauseCount mirrors are paused and $CopyingCount mirrors are syncing.
fi

rm -f xiostatus.tmp

Existe uma maneira mais elegante de contar e "variável-ize" essas contagens usando algo como awk? Neste caso, o arquivo é pequeno, então não é grande coisa, mas se o arquivo fosse 900mb, seriam necessários muitos ciclos extras para passar por ele 3 vezes ...

    
por LVLAaron 08.02.2012 / 01:48

3 respostas

13

awk pode substituir todo o script facilmente:

#!/usr/bin/awk -f

/Sync/ {SyncCount++}
/paused/ {PauseCount++}
/Copying/ {CopyingCount++}

END {
    if(SyncCount == 11)
        print "All 11 mirrors are in sync."
    else
        print (+PauseCount) " mirrors are paused and " (+CopyingCount) " mirrors are syncing."
}

O (+var) é forçar awk a tratar a variável como um número (portanto, a saída será 0 se a variável não foi definida). Você também pode usar um bloco BEGIN para definir todas as variáveis como 0 inicialmente:

BEGIN {
    SyncCount = PauseCount = CopyingCount = 0
}

Cole isso em um arquivo e execute awk -f /path/to/the/script.awk xiostatus.tmp . Se você não precisa do arquivo temporário, você pode até fazer /root/xiotech status | awk -f /path/to/the/script.awk .

Se você definir o bit de execução no script awk , poderá chamá-lo como executável autônomo: /path/to/the/script.awk xiostatus.tmp ou /root/xiotech status | /path/to/the/script.awk .

    
por 08.02.2012 / 02:03
4

Para aqueles que querem contar todas instâncias, aqui está uma versão do awk que contará várias não-sobrepostas quando houver mais de um na mesma linha

UPDATE: Eu incluí agora outro método que usa split(... . É muito mais rápido que o método match( substr(... , que agora está listado abaixo do mais rápido. O método split(... é mais de 4 vezes mais rápido que o outro ... (testado em 87 arquivos para um total de 407.612 linhas). Para uma comparação adicional, o método Michael Mrozek's , usando /Sync/ range-selection (que conta linhas contendo cada padrão vs. contando todas as instâncias do padrão) é duas vezes mais rápido que este novo método (para os mesmos dados).

Outro benefício de franja (?) deste mais rápido split(methos) é que ele é bastante tolerante a caracteres UTF-8 inválidos no arquivo (contanto que eles não estejam no padrão delimitador) ... Os delimitadores são os próprios padrões de string reais sendo contados ... Vários dos meus arquivos de teste tinham UTF-8 inválido neles e levei um bom tempo para descobrir por que obtive resultados diferentes dos dois métodos.
Depois que os arquivos com problemas foram recodificados para validar o UTF-8, ambos os métodos produzem resultados idênticos.

Aqui está o novo método mais rápido (4+ vezes mais rápido) ... usando split(...

#!/bin/bash
pat='xx|yy|zz'
awk -v vpat="$pat" 'BEGIN { 
  split(vpat, pat, "|"); for(i in pat) pz++ 
} 
{ if (NF) { for( p in pat ) { ct[p]+=(split( $0, A, pat[p] ) -1) }}
}
END { print " count   pattern"
      for (p=1; p<=pz; p++) { printf "%6d   %s\n", +ct[p], pat[p] } 
}' file

Aqui está o método mais lento. usando match( substr(...

#!/bin/bash
# Count occurrences of multiple non-overlapping string patterns
awk 'BEGIN {
  pattern[1]="xx"
  pattern[2]="yy"
  pattern[3]="zz"
}
{ for( p in pattern ) {
    LHB=0; RSTART=RLENGTH=1
    while( match( substr( $0, LHB+=(RSTART+RLENGTH-1)), pattern[p] )){
      count[p]++ 
    }
  }
} END {
  print "occurs  pattern"
  for (p in pattern) {
    printf "%6d  %s\n", +count[p], pattern[p] 
  } 
}' file

Aqui está o arquivo de entrada

xx xx  xx
xx             yy           xx

A saída é a seguinte:

occurs  pattern
     5  xx
     1  yy
     0  zz
    
por 08.02.2012 / 11:18
3

Que tal:

eval '/root/xiotech status | grep -Eo 'Sync|paused|Copying' | sort | uniq -c | 
    awk '{print "count_" $2 "=" $1}''
if [ "$count_Sync" -eq 11 ]; then
    echo All 11 mirrors are in sync.
else
    echo $count_paused mirrors are paused and $count_Copying mirrors are syncing.
fi

O grep -Eo permite pesquisar vários padrões (separados por "|") e retorna apenas a sequência que correspondeu. O sort | uniq -c mostra a contagem das palavras encontradas. O script awk formata novos comandos shell para criar variáveis começando com "count_". E finalmente, o eval tomará os comandos do shell criados e os avaliará dentro do shell.

    
por 08.02.2012 / 02:04