Как захватить строки между двумя строками из файла, но только последнее вхождение?

У меня есть файл журнала, который выводится скриптом, файл журнала поворачивается ежедневно. Он будет содержать строки

Transfer started at timestamp 

и

Transfer completed successfully at timestamp

несколько раз, так как упомянутая передача будет выполняться ежечасно. Временные метки ранее были созданы с помощью date.

Я хочу захватить последний экземпляр этих двух строк и все между ними в отдельный файл. Если начальная строка найдена в конце файла журнала, без следующей завершенной строки, я хочу захватить все до EOF и вывести сообщение об ошибке, чтобы сказать, что конечная строка не найдена.

Я предполагаю, что мне нужно будет использовать sed или awk, но я действительно неопытен с ними. Я хочу использовать команду в сценарии bash и понять, что делает каждая часть, поэтому некоторые объяснения будут очень полезны.

Примерный фрагмент файла журнала:

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
...
[d8 ] Желаемый результат:

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

Однако, если файл журнала был таким:

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.

Я хотел бы вывести:

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
1
задан 20 June 2016 в 12:33

3 ответа

Python все, но пусть регулярные выражения выполняют вашу работу!

Вставьте скрипт ниже в любой файл, например. logfilter.py и сделать его исполняемым с помощью команды chmod +x logfilter.py.

Тогда вы можете запустить его так, если предположить, что он находится в текущем каталоге:

./logfilter.py logfile.txt

Это сделает он обрабатывает файл logfile.txt.

Однако, если вы не передадите ему какие-либо аргументы командной строки, он будет ожидать данных на стандартном вводе. Это означает, что вы также можете передавать данные в него. В следующем примере обрабатываются данные из буфера обмена (требуется xsel для доступа к буферу):

xsel -ob | ./logfilter.py

Сценарий:

#! /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)
1
ответ дан 23 May 2018 в 09:20

Вы можете использовать массив awk с переключателем для буферизации последнего блока и напечатать текст ошибки, если переключатель все еще установлен в конце (это, по-моему, реализация awk @ python ответа anatoly_techtonik): [ ! d0]

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
1
ответ дан 23 May 2018 в 09:20

Просто используйте Python. У меня действительно нет времени, но я бы начал с этого:

#!/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")
1
ответ дан 23 May 2018 в 09:20
  • 1
    Спасибо за ответ, я слишком стар для домашней работы! Я действительно искал команду в сценарии bash, поэтому Python не является моим предпочтительным вариантом. Чтобы узнать, что дальше, после того, как вы справитесь с большим количеством вещей bash! – Arronical 14 June 2016 в 18:39
  • 2
    @Arronical bash классный, но нечитаемый. И этот материал работает на любой платформе с Python и, возможно, даже на MicroPython. Отредактировано, чтобы добавить обработку ошибок, если вы не делаете домашнее задание. знак равно – anatoly techtonik 18 June 2016 в 12:26

Другие вопросы по тегам:

Похожие вопросы: