Script Bash para obter contexto anterior específico

0

Estou procurando em alguns arquivos de log onde há grupos de ações executadas. No início de cada grupo, há uma linha que contém informações sobre o grupo e, em seguida, várias informações detalhadas sobre cada ação são impressas com um status de PASSA / FALHA impresso no final de cada teste individual.

O que eu quero fazer é encontrar qualquer ação que falhe e imprimir a linha de cabeçalho seguida por alguma quantidade de contexto antes da linha de falha.

Por exemplo:

Start test group ID 12345
verbose info
verbose info
Test 1 PASSED
verbose info
verbose info
Test 2 PASSED
Start test group ID 238284
verbose info
verbose info
Test 1 PASSED
verbose info
verbose info
Test 2 FAILED

O acima seria condensado em

Start test group ID 238284
verbose info
verbose info
Test 2 FAILED

O número de linhas antes do sinalizador FAILED não é constante para cada teste, e cada teste tem um comprimento médio diferente também, mas um número constante é bom para mim. Eu geralmente me preocupo com as últimas linhas de qualquer maneira.

Eu sinto que isso pode ser um pouco complicado para o grep, mas eu nunca usei o awk para nada e não sei por onde começar.

    
por ben 14.06.2012 / 18:17

4 respostas

3

Aqui está uma solução de awk que simplifica as coisas processando a saída na ordem inversa (requer o comando tac , que é parte dos GNU coreutils):

Primeiro, o script awk (coloque em um arquivo como 'process.awk'). É um pouco longo demais para um bash one-liner.

BEGIN                           { output=0; any=0; }
/^Test .* FAILED/               { output=1; any=1; }
/^Test .* PASSED/               { output=0; }
/^Start test group/ && any == 1 { output=1; any=0; }
output == 1                     { print; }

Em seguida, execute esse script no arquivo de log invertido e inverta a saída:

tac logfile | awk -f process.awk | tac

Como funciona?

Primeiro, passamos nossa entrada através de tac para reverter a ordem das linhas (para que possamos determinar se as linhas "a seguir" pertencem a um teste FAILED ou PASSED antes de lê-las).

O script funciona da seguinte maneira. Cada ação consiste em uma condição que deve ser correspondida, seguida por um bloco de código a ser executado se a linha atual corresponder à condição.

A primeira ação é uma ação BEGIN, que é sempre executada uma vez antes de começarmos a examinar a entrada. Inicializou duas flags booleanas que controlam o que é impresso. output será definido como 1 se quisermos imprimir a linha atual, 0 caso contrário. any será definido como 1 sempre que encontrarmos um teste FAILED e redefinido como 0 depois que terminarmos de processar um grupo de teste. Ambos os valores começam em 0.

A próxima ação testa a linha atual para ver se é o início de um teste com falha (lembre-se de que estamos processando a saída na ordem inversa). Em caso afirmativo, defina os dois output e any .

A próxima ação testa a linha atual para ver se é o início de um teste passado. Nesse caso, limpe o sinalizador output , mas deixe any sozinho. (Ainda pode haver um teste com falha antes do final do grupo de teste).

A próxima ação testa a linha atual para ver se é o cabeçalho do grupo de teste e se o sinalizador any está definido. Se estiver, queremos imprimir o cabeçalho (tivemos pelo menos um teste com falha), então defina output e limpe any (para preparar o próximo grupo de teste). Caso contrário, não precisamos fazer nada; any já é 0 e output não pode ter sido definido como 1 se any nunca foi.

Por fim, temos uma ação que não examina a linha atual, mas apenas verifica se alguma das ações anteriores definiu output . Se tiverem, imprimimos a linha atual (que pode ser uma linha "Teste FALHADO", algumas informações detalhadas que "precedem" a linha FAILED ou um cabeçalho de grupo de teste).

Quando todas as ações estiverem esgotadas, passamos para a próxima linha de entrada e tentamos aplicar cada ação novamente. Depois que toda a entrada estiver esgotada, teremos impresso cada uma das linhas de saída que queremos, mas na ordem inversa. Piping a saída através de tac corrige isso.

Observe que o script pode ser um pouco mais eficiente ao custo de torná-lo mais complexo, mas deve ser rápido o suficiente.

    
por 14.06.2012 / 19:28
1

Um script derivado do script do Bgs no bash:

buffer=""; cat /your/file | while read line
do
    echo $line | grep -Eq "^Start" && start=$line && continue
    echo $line | grep -q "FAILED" && echo -e "$start$buffer\n$line" \
            && buffer="" && continue
    echo $line | grep -q "PASSED" && buffer="" || buffer="$buffer\n$line"
done

Para cada linha "FAILED", a linha "Start" junto com todas as linhas anteriores à linha "FAILED" são impressas até a última linha "FAILED" ou "PASSED" (excluindo).

Exemplo de arquivo de entrada:

Start test group ID 12345
verbose info #1
verbose info #2
Test 1 PASSED
verbose info #3
verbose info #4
Test 2 PASSED
verbose info #5
verbose info #6
Test 3 PASSED
verbose info #7
verbose info #8
verbose info #9
Test 4 FAILED
Start test group ID 98765
verbose info #10
verbose info #11
verbose info #12
Test 5 FAILED
verbose info #13
verbose info #14
Test 6 PASSED
verbose info #15
verbose info #16
verbose info #17
Test 7 FAILED
verbose info #18
verbose info #19
verbose info #20
Test 8 PASSED

Saída de script:

Start test group ID 12345
verbose info #7
verbose info #8
verbose info #9
Test 4 FAILED
Start test group ID 98765
verbose info #10
verbose info #11
verbose info #12
Test 5 FAILED
Start test group ID 98765
verbose info #15
verbose info #16
verbose info #17
Test 7 FAILED
    
por 14.06.2012 / 19:57
0

Pensando em ideias ... há algumas maneiras de você chegar aqui

  1. Use o AWK para capturar as linhas de partida e, em seguida, pegue a partir da última linha PASSED ou FAILED até obter um FAILED no ponto em que despejar a linha Start e o último pacote que leva à linha FAILED

  2. Ou, filtre as linhas Iniciar e o contexto FAILED separadamente com grep e mescle-os. Para isso, você precisará manter números de linha.

Experimente este AWK bruto como um começo,

# script.awk
BEGIN {buffer1="";buffer2=""}
{ 
 if ($1 == "Start") 
 {
  buffer1=$0
 } 
 else 
 { 
  if ($3 == "PASSED") 
  {
   buffer2=""
  } 
  else 
  {
   buffer2=buffer2 "\n" $0; 
   if ($3 == "FAILED") 
   {
    printf "%s%s\n",buffer1,buffer2
   }
  }
 }
}

Executar com awk -f script.awk file.txt

Notas:

  1. Isso precisará de ajustes se as linhas Start , PASSED ou FAILED forem diferentes
    • bem simples se forem tão consistentes quanto no seu exemplo
  2. Isso também pode falhar se suas verbose partes tiverem uma das três palavras-chave "acima" no lugar "certo".
    • Em caso afirmativo, você precisará adicionar mais contexto para capturar as palavras-chave certas
  3. Isso fará com que você tenha todas as linhas da seção FAILED
    • você pode brincar um pouco com os buffers para obter dados menores
por 14.06.2012 / 18:50
0

Uma solução bash simples (salvei seu exemplo em foo.txt):

buff="" ; cat foo.txt| while read line; do echo $line| grep -q "^Start" && buff="" ; buff="$buff\n$line" ; echo $line | grep -q FAILED && echo -e $buff; done
    
por 14.06.2012 / 19:12

Tags