Como capturar linhas entre duas strings de um arquivo, mas apenas a última ocorrência?

4

Eu tenho um arquivo de log que é gerado por um script, o arquivo de log é rotacionado diariamente. Ele conterá as strings

Transfer started at timestamp 

e

Transfer completed successfully at timestamp

repetidamente, como a transferência mencionada ocorrerá de hora em hora. Os timestamps terão sido criados anteriormente com date .

  • Eu quero capturar a última instância dessas duas cadeias e tudo no meio, em um arquivo separado.
  • Se a cadeia iniciada for encontrada perto do final do arquivo de log, sem sequência completada, eu quero capturar tudo até EOF e gerar uma mensagem de erro para dizer que a string final não foi encontrada.

Suponho que precisarei usar sed ou awk , mas sou muito inexperiente com eles. Eu quero usar o comando em um script bash, e entender o que cada parte está fazendo, então algumas explicações seriam muito úteis.

Um pedaço de arquivo de log de exemplo:

ERROR - Second tech sync failed with rsync error code 255 at Fri May 27 13:50:4$
--------------------------------------------------------------------
After_sync script completed successfully with no errors.
Main script finished at Fri May 27 13:50:43 BST 2016 with PID of 18808.
--------------------------------------------------------------------
Transfer started at Fri May 27 13:50:45 BST 2016
Logs transferred successfully.
Images transferred successfully.
Hashes transferred successfully.
37 approvals pending.
Transfer completed successfully at Fri May 27 14:05:16 BST 2016
--------------------------------------------------------------------
Local repository verification started at Fri May 27 14:35:02 BST 2016
...

A saída desejada:

Transfer started at Fri May 27 13:50:45 BST 2016
Logs transferred successfully.
Images transferred successfully.
Hashes transferred successfully.
37 approvals pending.
Transfer completed successfully at Fri May 27 14:05:16 BST 2016

No entanto, se o arquivo de log for assim:

ERROR - Second tech sync failed with rsync error code 255 at Fri May 27 13:50:4$
--------------------------------------------------------------------
After_sync script completed successfully with no errors.
Main script finished at Fri May 27 13:50:43 BST 2016 with PID of 18808.
--------------------------------------------------------------------
Transfer started at Fri May 27 13:50:45 BST 2016
Logs transferred successfully.
Images transferred successfully.
Hashes transferred successfully.

Eu gostaria de produzir:

Transfer started at Fri May 27 13:50:45 BST 2016
Logs transferred successfully.
Images transferred successfully.
Hashes transferred successfully.
ERROR: transfer not complete by end of log file
    
por Arronical 14.06.2016 / 16:51

4 respostas

2

Quando ouço "Eu quero fazer X com o último algo no arquivo", eu acho:

  • inverta o arquivo
  • faça X com o primeiro algo no arquivo
  • inverta a saída de X

no código:

tac logfile | awk '
    BEGIN {text = "ERROR: transfer not complete by end of log file"}
    /^Transfer completed successfully/ {text = ""}
    {text = text ORS $0}
    /^Transfer started at / {print text; exit}
' | tac

Como estamos lendo o arquivo de log de baixo para cima, começo assumindo que a transferência não está completa. Se eu vir a mensagem "transferência concluída", podemos descartar tudo o que capturamos até agora. Nós salvamos cada linha. Quando vemos a linha "transferência iniciada", sabemos que vimos toda a transferência última no arquivo: imprima o texto capturado (invertido) e saia do awk.

    
por glenn jackman 14.06.2016 / 17:31
1

Python todas as coisas, mas deixe expressões regulares fazer o trabalho para você!

Cole o script abaixo em qualquer arquivo, por exemplo logfilter.py e torná-lo executável usando o comando chmod +x logfilter.py .

Então você pode executá-lo assim, supondo que esteja localizado no diretório atual:

./logfilter.py logfile.txt

Isso fará com que ele processe o arquivo logfile.txt .

No entanto, se você não passar nenhum argumento da linha de comando, ele aguardará os dados na entrada padrão. Isso significa que você também pode enviar dados para ele. O exemplo a seguir processa dados da área de transferência (precisa de xsel instalado para acessar a área de transferência):

xsel -ob | ./logfilter.py

O script:

#! /usr/bin/env python3

p_start = r'^Transfer started at .*?$'
p_end   = r'^Transfer completed successfully at .*?$'

error_no_match = 'ERROR: no match found'
error_no_end   = 'ERROR: transfer not complete by end of log file'

pattern = r'{p0}(?!.*{p0})(?:.*?{p1}|.*)'.format(p0=p_start, p1=p_end)

import sys, re
if len(sys.argv) > 1:
    with open(sys.argv[1]) as f:
        text = f.read()
else:
    text = sys.stdin.read()

matches = re.findall(pattern, text, re.DOTALL | re.MULTILINE)
if matches:
    last_match = matches[-1]
    print(last_match)
    if not re.search(p_end, last_match, re.DOTALL | re.MULTILINE):
        print(error_no_end)
else:
    print(error_no_match)
    
por Byte Commander 14.06.2016 / 18:02
1

Você poderia usar uma matriz awk com um toggle para armazenar em buffer o último bloco e imprimir o texto de erro se a alternância ainda estiver definida no final (isso é essencialmente uma implementação awk da resposta python do @ anatoly_techtonik, eu acho): / p>

awk '
  BEGIN{PROCINFO["sorted_in"]="@ind_num_asc"}

  /Transfer started/ {inblock=1; delete a;}
  /Transfer completed/ {a[FNR]=$0; inblock=0;}

  inblock == 1 {a[FNR]=$0}

  END {
    for (i in a) print a[i]; 
    if (inblock) 
      print "ERROR: transfer not complete by end of log file"
  }
' logfile
    
por steeldriver 14.06.2016 / 20:40
1

Use apenas o Python. Eu realmente não tenho tempo, mas eu começaria com isso:

#!/usr/bin/env python

start = "Transfer started at"
end = "Transfer completed successfully"
buffer = ""
log = False

for line in open('logfile.log'):
  if line.startswith(start):
    buffer = line
    log = True
  elif line.startswith(end):
    buffer += line
    log = False
  elif log:
    buffer += line

open('output.log', 'w').write(buffer)

if log == True:
  print("End string was not found")
    
por anatoly techtonik 14.06.2016 / 17:26