Imprima uma linha somente se a próxima linha NÃO contiver uma correspondência específica

12

Estou tentando pesquisar um arquivo de log em busca de atividades registradas que não foram concluídas. Por exemplo, registro uma "Atividade inicial para o ID 1234 ..." e, se for bem-sucedida, a próxima linha será "Atividade 1234 concluída".

Estou tentando obter as linhas "Iniciando ..." que NÃO são seguidas pelas linhas "Concluídas" correspondentes.

Arquivo de log de exemplo

Starting activity for ID 1234
ID 1234 completed successfully
Starting activity for ID 3423
ID 3423 completed successfully
Starting activity for ID 9876
ID 9876 completed successfully
Starting activity for ID 99889
ID 99889 completed successfully
Starting activity for ID 10011
ID 10011 completed successfully
Starting activity for ID 33367
Starting activity for ID 936819
ID 936819 completed successfully

Neste exemplo, eu estaria procurando pela saída:

Starting activity for ID 33367

... porque não é seguido por uma linha "concluída".

Eu tentei fazer isso com grepe awk, mas não tive muito sucesso. Suponho que isso possa ser feito com uma dessas ferramentas, mas as minhas grepe as awkcosteletas não são avançadas.

Olhando para uma rápida e confiável grepou awkpadrão para dar os resultados que eu preciso aqui.

PattMauler
fonte
Não acho que seja fácil com o grep + awk, mas você pode explicar um pouco sobre por que está fazendo isso? Uma saída de todas as atividades em execução, por exemplo, sucesso ou não finalização?
30512 margarida
@ warl0ck, estou procurando o "não terminado".
PattMauler

Respostas:

10

Aqui está uma awkalternativa:

awk '
  /^Starting/ { I[$5] = $0                  }
  /^ID/       { delete I[$2]                }
  END         { for (key in I) print I[key] }
' infile

Resultado:

Starting activity for ID 33367

A Imatriz associativa controla quais IDs foram vistos.

Thor
fonte
Isso funciona muito bem, pois até parece acomodar situações em que as linhas de log "Iniciando ..." e "Concluído ..." não são adjacentes / sequenciais. Obrigado @Thor!
PattMauler
Não há de quê. Isso deve funcionar eficientemente com a entrada de tamanho (quase) arbitrária, já que sempre armazena o ID e o tempo de pesquisa é O (1).
Thor
Agradável. Apenas uma coisa: como aprendi com @RobertL ( unix.stackexchange.com/a/243550/135943 ), você não precisa atribuir um valor para criar um elemento de matriz. Então, em vez de I[$5] = 1, você pode apenas usar I[$5]. (Você não se importa com o valor, você apenas quer que o elemento exista , e simplesmente nomeá-lo para fazer isso.) #
Wildcard
@ Wildcard: Você está certo, mas depois de revisar a pergunta do OP e a saída grep como ele está procurando, é mais apropriado lembrar de toda a linha e da saída que no final.
Thor
3
sed '$!N;/\n.*completed/d;P;D' <input

Isso excluirá da saída todas as linhas de entrada que não forem seguidas por uma linha correspondente à sequência concluída .

mikeserv
fonte
2

Veja como você pode fazer isso com o GNU sed:

sed -r 'N; /([0-9]+)\n\w+\s+\1/d; P; D' infile
  • N lê mais uma linha no espaço do padrão.
  • O regex de correspondência verifica se IDs idênticos são encontrados, se o espaço do padrão é excluído ( d) e o ciclo é reiniciado.
  • Se não corresponder, imprima a primeira linha no espaço do padrão ( P) e exclua-a ( D).
Thor
fonte
Não vejo nada estendido aqui ... então -rnão é necessário, certo?
Louis Maddox
1
@ mmmm: é necessário porque, caso contrário, o grupo de captura precisa ser escapado, e o mesmo vale para o +quantificador.
Thor
Ah ok! Eu o modifiquei e me disseram que não era necessário, obrigado por esclarecer.
Louis Maddox
1

se sua instalação suportar pcregrep, a opção multilinha (-M) será útil.

pcregrep -M -o '\AStarting activity for ID (\d+)\n(?!ID \1)' t.z

Iniciando atividade para ID 33367

iruvar
fonte