Подтвердить что ты не робот

Как реализовать питонический эквивалент хвоста -F?

Что такое pythonic способ просмотра хвоста растущего файла для появления определенных ключевых слов?

В оболочке я могу сказать:

tail -f "$file" | grep "$string" | while read hit; do
    #stuff
done
4b9b3361

Ответ 1

Ну, самым простым способом было бы постоянно читать из файла, проверять, что нового и проверить для хитов.

import time

def watch(fn, words):
    fp = open(fn, 'r')
    while True:
        new = fp.readline()
        # Once all lines are read this just returns ''
        # until the file changes and a new line appears

        if new:
            for word in words:
                if word in new:
                    yield (word, new)
        else:
            time.sleep(0.5)

fn = 'test.py'
words = ['word']
for hit_word, hit_sentence in watch(fn, words):
    print "Found %r in line: %r" % (hit_word, hit_sentence)

Это решение с readline работает, если вы знаете, что ваши данные будут отображаться в строках.

Если данные представляют собой какой-то поток, вам нужен буфер, который больше, чем самый большой word, который вы ищете, и сначала заполните его. Это немного усложняется...

Ответ 2

def tail(f):
    f.seek(0, 2)

    while True:
        line = f.readline()

        if not line:
            time.sleep(0.1)
            continue

        yield line

def process_matches(matchtext):
    while True:
        line = (yield)  
        if matchtext in line:
            do_something_useful() # email alert, etc.


list_of_matches = ['ERROR', 'CRITICAL']
matches = [process_matches(string_match) for string_match in list_of_matches]    

for m in matches: # prime matches
    m.next()

while True:
    auditlog = tail( open(log_file_to_monitor) )
    for line in auditlog:
        for m in matches:
            m.send(line)

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

Ответ 3

вы можете использовать pytailf: Простая оболочка python tail -f

from tailf import tailf    

for line in tailf("myfile.log"):
    print line

Ответ 4

Вы можете использовать select для опроса для нового содержимого в файле.

def tail(filename, bufsize = 1024):
    fds = [ os.open(filename, os.O_RDONLY) ]
    while True:
        reads, _, _ = select.select(fds, [], [])
        if 0 < len(reads):
            yield os.read(reads[0], bufsize)

Ответ 5

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

Либо откройте файл с помощью O_NONBLOCK и используйте select для опроса для доступности чтения, а затем read для чтения новых данных и строковых методов для фильтрации строк в конце файла... или просто используйте модуль subprocess, и пусть tail и grep выполняют для вас работу так же, как и в оболочке.

Ответ 7

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

Это должно работать:

import sys

needle = "needle"

blocks = []

inf = sys.stdin

if len(sys.argv) == 2:
    inf = open(sys.argv[1])

while True:
    block = inf.read()
    blocks.append(block)
    if len(blocks) >= 2:
        data = "".join((blocks[-2], blocks[-1]))
    else:
        data = blocks[-1]

    # attention, this needs to be changed if you are interested
    # in *all* matches separately, not if there was any match ata all
    if needle in data:
        print "found"
        blocks = []
    blocks[:-2] = []

    if block == "":
        break

Задача состоит в том, чтобы обеспечить совпадение иглы, даже если она разделена двумя границами блоков.

Ответ 8

Если вам просто нужно простое решение Python 3 для обработки строк текстового файла по мере их написания, и вам не нужна поддержка Windows, это сработало для меня:

import subprocess
def tailf(filename):
    #returns lines from a file, starting from the beginning
    command = "tail -n +1 -F " + filename
    p = subprocess.Popen(command.split(), stdout=subprocess.PIPE, universal_newlines=True)
    for line in p.stdout:
        yield line
for line in tailf("logfile"):
    #do stuff

Он блокирует ожидание записи новых строк, поэтому это не подходит для асинхронного использования без каких-либо изменений.

Ответ 9

Насколько мне известно, в списке функций Python нет эквивалента "tail". Решение было бы использовать tell() (получить размер файла) и read() для разработки конечных строк.

Это сообщение в блоге (не я) имеет функцию, написанную, выглядит подходящей для меня! http://www.manugarg.com/2007/04/real-tailing-in-python.html

Ответ 10

Вы можете использовать collections.deque для реализации хвоста.

Из http://docs.python.org/library/collections.html#deque-recipes...

def tail(filename, n=10):
    'Return the last n lines of a file'
    return deque(open(filename), n)

Конечно, это читает все содержимое файла, но это аккуратный и сложный способ реализации хвоста.