O uso do awk para processar vários arquivos precisa contar a ocorrência da variável após o padrão. Como posso parar a redefinição da matriz após cada arquivo?

1

Eu quero contar todas as ocorrências de qualquer variável entre parênteses após padrões, para arquivos em um diretório que tenha uma extensão específica. Cada arquivo pode conter o padrão em vários registros / linhas. Primeiro tente com awk e bash e lutando depois de ter passado dias nisso.

Até agora, posso processar os arquivos e armazenar os resultados em uma matriz, mas parece que a matriz é sobrescrita a cada novo arquivo que processo. Como posso manter o valor da matriz?

#!/bin/bash
for x in 'find . $PROGFILES -name "*.fgl"'
do

    awk -f <(cat -  <<-'EOF'
        / envget | env-get | \"envget\" | \"env-get\" /  
        {
        gsub( /get-env/, "envget")              ;# removes hypens
        gsub( /.*envget/, " envget")
        gsub( "\concat" ,"")       ;# removes concat
        gsub( "\substring" , "")   ;# removes substring
        for (i = 1; i<= NF; i++) {
            if ( substr( $i, 1, 6) == "envget" ) {
                    lenofget = 8;
                } else {
                    lenofget = 0;
                }
                if ( lenofget != 0 ) {
                    gsub("\envget" , "",$i)    ;#removes envget
                    gsub ( /\)\.*/, "",$i)      ;#removes everything after a closing parenthesis
                    gsub ( //, "",$i)        ;#so used octal instead
                    gsub ( /\(/, "",$i)         ;#removes paraentheses
                    gsub ( /\"/, "",$i)         ;#removes double quotes
                    gsub ( /\,.*/, "",$i)       ;#removes everything after a , This is for any concat syntax
                    gsub ( /[\/].*/, "",$i)     ;#removes everything after a forward slash
                    narr[$i]++
                }
            }
        }
        END {
            for (y in narr) {
                printf("%s - %d\n",y, narr[y])

            }   
        }   
EOF
) $x
done

Registros / linhas típicos em um arquivo com o padrão / s seriam:

if envget("SYPSDATA") in {SPACES "."}
    set lf-path = "envget"('SYPSCTRL')
if env-get(concat("LOG_PRINTER",service-centre)) != spaces
trconcat(env-get("TMPDIR"),"/ps_xxx_temp.psv")
envget(substring(ws-envprinter1,1,strlen(ws-envprinter1)))
      set lf-path = "envget"('SYPSCTRL')
            display bitmap concat(envget('BTS')'/images/repedge.gif') @19,44

Dado que há vários arquivos com várias linhas correspondentes padrão, estou esperando obter uma saída como essa (em que os números são contados em todos os arquivos).

BTS - 15
LOG_PRINTER - 7
ws-envprinter1 - 3
SYPSDATA - 120
TMPDIR - 130
SYPSCTRL - 200

Tenho certeza que isso deve ser óbvio, mas e que provavelmente há uma maneira muito mais simples de fazer isso, então qualquer conselho é muito apreciado. Meu cérebro dói - tire meu chapéu para vocês / garotas que fazem esse tipo de coisa o tempo todo.

    
por BoRain 14.11.2017 / 04:46

2 respostas

1

Você está fazendo

for x in 'find . $PROGFILES -name "*.fgl"'
do
    awk (awk_program) $x
done
que inicia um novo processo awk para cada arquivo. Por quê? Apenas faça
awk (awk_program) *.fgl "$PROGFILES"/*.fgl

, a menos que você precise pesquisar subdiretórios. Se você precisa pesquisar subdiretórios, é um pouco mais complicado:

find . "$PROGFILES" -name "*.fgl" -exec awk (awk_program) {} +

Notas:

  • Você deve sempre citar as variáveis do shell (como "$PROGFILES" e "$x" ) a menos que você tenha uma boa razão para não e você tem certeza que sabe o que está fazendo.
  • Você não precisa usar cat desse jeito. Você pode colocar o programa awk entre aspas:

    awk '
            / envget | env-get | \"envget\" | \"env-get\" /  
            {
                gsub( /get-env/, "envget")
                            ︙
            }
        ' "$x"
    

    ou você pode colocar em um arquivo e dizer awk -f (awk_program_file) .

  • Nenhuma das abordagens acima é garantida para obter as contagens totais porque há um limite (muito grande) no tamanho de uma linha de comando. Se você tem tantos arquivos que o comprimento combinado de seus nomes excede esse limite, find invocará vários processos awk para cobrir todos os nomes, e você voltará a receber contagens incompletas. Uma maneira de lidar com isso seria para coletar as saídas dos awk de execuções individuais e combiná-las.
por 14.11.2017 / 05:11
0

Existem vários erros em sua tentativa (não tentando ser condescendente, estamos todos aqui para aprender!).

Toda invocação de awk é um processo diferente, com seu próprio espaço de memória, portanto, depois que um arquivo é processado, é normal que a próxima invocação de awk não retenha o valor de sua matriz. Você precisa contar em cada iteração de seu for , e um passo extra no final para resumir tudo. A maneira mais fácil é adicionar tudo isso em um arquivo:

#!/bin/bash

echo "" > "$HOME/tmp_count.txt"

for x in 'find . $PROGFILES -name "*.fgl"'
do

    awk '
        /env-?get/  {
          for (i = 1; i<= NF; i++) {
            if ($i ~ /env-?get/) {
              a = gensub(/.*env-?get\"?\((concat\(|substring\()?(\"|)?([a-zA-Z0-9\-_]*)(\"|)? *(\)|,)?.*/, "\3", $i)
              arr[a]++
            }
          }
        }
        END {
            for (y in arr) {
                printf("%s %d\n",y, arr[y])
            }   
        }
        ' "$x" >> "$HOME/tmp_count.txt"
done

awk '{arr[$1] += $2}END{for (key in arr) {printf("%s - %d\n", key, arr[key])}}' < "$HOME/tmp_count.txt"

Além disso, o seu método parece não funcionar o tempo todo, então eu tentei usar um grande regexp em gensub , ele funciona pelo menos com a amostra que você forneceu. Eu não sou nenhum assistente de regexp, por isso pode quebrar em certas ocorrências. Experimente e deixe-me saber se funcionou para você!

    
por 14.11.2017 / 14:38