Como identificar, classificar em ordem decrescente e exibir os 10 primeiros blocos de texto, por categoria

0

O histórico de transações feitas em uma entidade dentro de nossos sistemas é semelhante ao seguinte:

   1 BYM1 TSTAB 09NOV 0035 CAB
Sometext 01
   2 BYM1 TSTAB 09NOV 0035 CAB
Can be done - question   
   3 BYM1 TSTAB 09NOV 0035 CAB
Sometext 02
Sometext 03
   6 BYM3 TSTAA 09NOV 0400 CAA
Some 04 text 04
   7 BYM3 TSTAA 10NOV 0455 CAC
Sometext 06
Sometext 06 line 2
   8 BYM3 TSTAA 10NOV 0455 CAC
Sometext 07
   9 BYM2 TSTAC 10NOV 0619 CAD
Some 08 text 0008 ABCD
Some 08 text 0008 BB00
Some 08 text 0008 CC00
Some 08 text 0008 DD00
Some 08 text 0008 EE00
  10 BYM2 TSTAC 10NOV 0627 CAD
Something BBBBBSSDGFSDSF
  11 BYM2 TSTAC 10NOV 0627 CAD
Something else
  12 BYM2 TSTAC 10NOV 0627 CAD
What text here
  13 BYM4 TSTAC 10NOV 0711 CAD
Tired figuring out
  19 BYM3 TSTAA 11NOV 0438 CAE
Some 04 text 05 05 05
  20 BYM3 TSTAA 11NOV 0441 CAF
Not so confidential now
  21 BYM3 TSTAA 11NOV 0441 CAF
Some 00 text 0009 X1X2
  43 BYM3 TSTAA 11NOV 0441 CAD
Some 0A text 0009 ABCD
  44 BYM3 TSTAA 11NOV 0441 CAD
Some 1B text
  45 BYM3 TSTAA 12NOV 1455 CAC
Something 0AADDBB
8782 BYM3 TSTAA 12NOV 1610 CAD
Something 0AADDBB
8830 BYM3 TSTAA 12NOV 1612 CAA
Something 0AADDBB
9999 BYM3 TSTAA 12NOV 1722 CAA
Something 0AADDBB

Os blocos de texto começam com a linha que tem um número nos primeiros 4 caracteres. (O número é, na verdade, um número de sequência em execução e toda transação é indexada com isso). A categoria (da transação) do bloco é definida pelos três últimos caracteres na linha que possui o número.

Estou procurando um script awk, sed (, vi, grep) para procurar blocos de texto pertencentes a uma "categoria", classificar os blocos resultantes em ordem decrescente do índice (números), exibir o número de blocos Eu pedi.

Por exemplo, se eu quiser pesquisar 4 blocos da categoria "CAD", a saída que gostaria de ver é:

8782 BYM3 TSTAA 12NOV 1622 CAD
Something 0AADDBB
  44 BYM3 TSTAA 11NOV 0441 CAD
Some 1B text
  43 BYM3 TSTAA 11NOV 0441 CAD
Some 0A text 0009 ABCD
  13 BYM4 TSTAC 10NOV 0711 CAD
Tired figuring out

Como posso conseguir isso? Qualquer ajuda seria muito apreciada: -)

    
por Laxii 17.10.2016 / 05:57

4 respostas

0

Para seguir o linux principe "uma tarefa - uma ferramenta":

  1. Imprime apenas o bloco necessário (como no exemplo CAD )

    sed '/^\s*[0-9].*CAD/!d;:a;N;/\n\s*[0-9]/! s/\n/\x0/;ta;P;D'

  2. Classifique na ordem inversa

    sort -rn

  3. Pegue apenas os primeiros blocos perguntados (como no exemplo 4 )

    head -4

Por favor, note que a maioria dos comandos linux operam com linhas (não blocos ) então os que foram convertidos em linhas alterando a linha \n ew para o símbolo nulo ( \x0 ) e então convertido de volta por tr .
Então, toda a linha:

sed '/^\s*[0-9].*CAD/!d;:a;N;/\n\s*[0-9]/! s/\n/\x0/;ta;P;D' test.txt |
sort -rn |
head -4 |
tr '
awk '
/^[ 0-9]{4} /{                 #for start block string
    if($NF==cat){              #if it is a needed block
        idx=$1
        BLOCK[idx]=$0          #put line onto array with asigned index
    }
    else
        idx=0                  #otherways asign index to 0
    next                       #end itteration, go to start with next line
}
idx{                           #pass inappropriate blocks (with 0-index)
BLOCK[idx]=BLOCK[idx] "\n" $0  #add line to array element with index
}
END{                           #when finish all lines
    for(i=0;i<num;i++){        #do num times
        max=0                  #asing 'max' variable to min value
        for(idx in BLOCK){     #for each index in array
            idx=idx+0          #convert string index into decimal
            if(idx>max)        
                max=idx        #find maximum index (field No.1 in block)
        }
        if(!max)             
            exit               #exit script if array empty (no more blocks)
        print BLOCK[max]       #print block with maximum index
        delete BLOCK[max]      #remove array element for furure search
     }
}' cat="CAD" num=4 test.txt
' '\n'

Eu gosto da resposta do G-Man para alterar R ow S eparator, mas isso não é muito adequado no caso. Mais simples de fazer isso de maneira comum

sed '/^\s*[0-9].*CAD/!d;:a;N;/\n\s*[0-9]/! s/\n/\x0/;ta;P;D' test.txt |
sort -rn |
head -4 |
tr '
awk '
/^[ 0-9]{4} /{                 #for start block string
    if($NF==cat){              #if it is a needed block
        idx=$1
        BLOCK[idx]=$0          #put line onto array with asigned index
    }
    else
        idx=0                  #otherways asign index to 0
    next                       #end itteration, go to start with next line
}
idx{                           #pass inappropriate blocks (with 0-index)
BLOCK[idx]=BLOCK[idx] "\n" $0  #add line to array element with index
}
END{                           #when finish all lines
    for(i=0;i<num;i++){        #do num times
        max=0                  #asing 'max' variable to min value
        for(idx in BLOCK){     #for each index in array
            idx=idx+0          #convert string index into decimal
            if(idx>max)        
                max=idx        #find maximum index (field No.1 in block)
        }
        if(!max)             
            exit               #exit script if array empty (no more blocks)
        print BLOCK[max]       #print block with maximum index
        delete BLOCK[max]      #remove array element for furure search
     }
}' cat="CAD" num=4 test.txt
' '\n'
    
por 17.10.2016 / 10:16
1

Aqui está uma solução para o gawk (GNU awk ; ou seja, a versão de awk encontrada na maioria dos sistemas "Linux"). Suponha que $cat esteja definido para a categoria que você deseja pesquisar e $num está definido para o número de registros que você deseja exibir.

awk -vRS='\n[ 0-9][ 0-9][ 0-9][0-9] ' -vcat="$cat" -vnum="$num" \
    '   BEGIN { first=1; rec_ind=0}
        {       if (first) {
                        rec = $0
                        first=0
                } else {
                        rec = save_seq $0
                }
                findnl = index(rec, "\n")
                if (findnl < 7) exit
                thiscat = substr(rec, findnl-3, 3)
                if (cat == thiscat) records[++rec_ind] = rec
                if (length(RT) == 0) {
                        # print "This should be the last record."
                        save_seq = "Does not matter"
                } else if (length(RT) == 6) {
                        save_seq = substr(RT, 2, 5)
                } else {
                        print "Invalid RT: len =", length(RT)
                        exit
                }
        }
        END   { num_recs = asort(records, sorted_records, "@val_num_desc")
                if (num < num_recs) num_recs = num
                for (i=1; i<=num_recs; i++) {
                        print sorted_records[i]
                }
              }
    '

Notas:

  • -vRS='\n[ 0-9][ 0-9][ 0-9][0-9] ' conjuntos variável RS (separador de registro) do awk para uma expressão regular que consiste em uma nova linha, seguido por um número sequencial inteiro de até quatro dígitos, seguido por um espaço. Eu incluí a nova linha porque seus dados têm números de quatro dígitos (seguido por espaços) no interior das linhas, onde eles não devem ser interpretados como separadores de registros. Note que esta regex é um pouco desleixada, pois aceita  007 e 12 4 .

    Definindo isso como separador de registro do awk significa que cada uma das suas "transações" será tratado como um único registro de awk, mesmo que contenha várias linhas. Existem algumas desvantagens:

    • Como o padrão RS inclui uma nova linha no início, o    1  no início dos seus dados não será reconhecido como um separador de registros.
    • Como esse é o padrão separador do registro, não é considerado parte do registro, mesmo que contenha informações vitais.

    Nós vamos lidar com esses problemas.

  • -vcat="$cat" e -vnum="$num" similarmente configuram as variáveis awk cat e num para os valores das variáveis de shell correspondentes.
  • BEGIN { first=1; rec_ind=0} inicializa o sinal first para true (1), para podermos reconhecer o primeiro registro e lidar com ele especialmente, e o índice de registro ( rec_ind ) para 0, para o acúmulo de registros que correspondem à categoria desejada.
  • if (first) é verdadeiro (estamos processando o primeiro registro), defina rec igual ao registro awk, $0 . Lembre-se, isso inclui todas as linhas até (mas não incluindo) a próxima linha que começa com um número de quatro dígitos. Além disso, inclui o número de quatro dígitos no início da primeira linha. Em seguida, definimos o sinal first como falso (0).

    Se este não for o primeiro registro, então está faltando seu número de quatro dígitos (porque esse é o registro separador ), então nós construímos o registro ( rec ) concatenando o número de sequência salvo ( save_seq ) com $0 . (Eu vou discutir save_seq momentaneamente.)

  • findnl = index(rec, "\n") encontra a primeira nova linha no registro (lembre-se, os registros contêm várias linhas). Se tiver menos de 7 caracteres desde o começo, então não há espaço para um número de sequência e uma categoria (sem sobreposição), e muito menos os outros campos, então isso é um erro. Caso contrário, extraia a categoria desse registro ( thiscat ) dos três últimos personagens antes da primeira nova linha - isto é, os últimos três caracteres da primeira linha da transação. Então, se thiscat corresponde à categoria que estamos procurando, salve o registro na matriz records .
  • RT é o terminador de registro - os caracteres que correspondem ao padrão RS no final do registro atual. Infelizmente, o terminador do registro atual é realmente o começo do próximo. Se o registro atual for o último, então RT será uma string vazia (comprimento 0); caso contrário, deve sempre ter 6 caracteres (uma nova linha, quatro caracteres que são espaços ou dígitos e um espaço). Extraia os últimos cinco caracteres (isto é, descarte a nova linha) e salve isso como save_seq , porque é o número de sequência da próxima transação.
  • Quando chegamos ao final dos dados, classificamos os registros (ordenando os valores, tratando-os como números, em ordem decrescente). Em seguida, imprima até num deles.
por 17.10.2016 / 09:38
0

Supondo que seu início de bloco pode ser detectado por uma linha de 6 campos começando com um número e que seus dados não contenham código de caractere $NF1 (control-a), por exemplo, você pode unir todas as linhas de um bloco em uma linha, substituindo as novas linhas por este código arbitrário. Em seguida, classifique as linhas, pegue as primeiras 4 e substitua o código por uma nova linha novamente.

#!/bin/bash
num=${1?number} cat=${2?category}
awk -vcat="$cat" '
 /^ *[0-9]+ / && NF==6 { ok = ($NF==cat) 
                         if(ok && sep!="")sep = "\n"
                       }
                    ok { printf "%s%s",sep,$0; sep = "
#!/bin/bash
num=${1?number} cat=${2?category}
awk -vcat="$cat" '
 /^ *[0-9]+ / && NF==6 { ok = ($NF==cat) 
                         if(ok && sep!="")sep = "\n"
                       }
                    ok { printf "%s%s",sep,$0; sep = "%pre%1" }
                   END { if(sep!="")printf "\n" }' |
sort -nr -k1,1 | head -"$num" |
tr '%pre%1' '\n'
1" } END { if(sep!="")printf "\n" }' | sort -nr -k1,1 | head -"$num" | tr '%pre%1' '\n'

O awk une as linhas se o campo sep (o último campo) for a categoria desejada. A variável "" está inicialmente vazia \n1 , em seguida, torna-se %code% dentro de um bloco e %code% quando um novo bloco é iniciado. No final, uma nova linha final é adicionada, a menos que não haja correspondências.

    
por 17.10.2016 / 10:40
-1

tente alterar os valores de v = e num =

$ awk '$NF==v{F=1;print;next}F&&NF!=6{print}F&&NF==6{F=0}' v="CAC" test.txt | awk '$NF~v{val=j++;F=1}F{Arr[val]=Arr[val]"\n"$0}END{n=asorti(Arr,S_Arr);for(i=n;i>=n-num;i--){print Arr[i]}}' v="CAC" num=4


  45 BYM3 TSTAA 12NOV 1455 CAC
Something 0AADDBB

   8 BYM3 TSTAA 10NOV 0455 CAC
Sometext 07

   7 BYM3 TSTAA 10NOV 0455 CAC
Sometext 06
Sometext 06 line 2

$ awk '$NF==v{F=1;print;next}F&&NF!=6{print}F&&NF==6{F=0}' v="CAD" test.txt | awk '$NF~v{val=j++;F=1}F{Arr[val]=Arr[val]"\n"$0}END{n=asorti(Arr,S_Arr);for(i=n;i>=n-num;i--){print Arr[i]}}' v="CAD" num=4


8782 BYM3 TSTAA 12NOV 1610 CAD
Something 0AADDBB

  44 BYM3 TSTAA 11NOV 0441 CAD
Some 1B text

  43 BYM3 TSTAA 11NOV 0441 CAD
Some 0A text 0009 ABCD

  13 BYM4 TSTAC 10NOV 0711 CAD
Tired figuring out
    
por 17.10.2016 / 07:27