特定のキーワードの出現について、成長するファイルの末尾を監視するPythonの方法は何ですか?
シェルで私は言うかもしれません:
tail -f "$file" | grep "$string" | while read hit; do
#stuff
done
さて、最も簡単な方法は、ファイルから絶えず読み取り、新機能を確認し、ヒットをテストすることです。
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
よりも大きいバッファーが必要であり、最初にそれを埋めます。そうすると少し複雑になります...
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を構成ファイルに保持して、複数の目的に使用できるようにします。私の拡張機能のリストには、単純な「in」一致ではなく正規表現のサポートがあります。
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)
使用できます pytailf :シンプルpython tail-fラッパー
from tailf import tailf
for line in tailf("myfile.log"):
print line
編集:以下のコメントにあるように、O_NONBLOCK
はディスク上のファイルでは機能しません。これは、他の誰かがソケットや名前付きパイプ、または別のプロセスからのデータのテールを探しに来た場合でも役立ちますが、尋ねられた実際の質問には答えません。元の答えは後世のために以下に残ります。 (tailとgrepへの呼び出しは機能しますが、とにかく一種の答えではありません。)
O_NONBLOCK
でファイルを開き、select
を使用して読み取りの可用性をポーリングしてから、read
を使用して新しいデータを読み取り、文字列メソッドを使用してファイルの最後の行をフィルタリングします...またはsubprocess
モジュールを使用してtail
とgrep
は、シェルの場合と同じように作業を行います。
そのためのパッケージがあるようです: https://github.com/kasun/python-tail
行ベースの読み取りで機能するように問題を制約できない場合は、ブロックに頼る必要があります。
これは機能するはずです:
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
課題は、2つのブロック境界で区切られている場合でも、針を確実に一致させることにあります。
書かれているテキストファイルの行を処理するための非常に単純な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
新しい行が書き込まれるのを待つのをブロックするため、これはいくつかの変更を加えずに非同期で使用するのには適していません。
私の知る限り、Python関数リストには「tail」に相当するものはありません。解決策は、tell()(ファイルサイズを取得)とread()を使用して終了行を計算することです。
このブログ投稿(私ではない)には関数が書き出されており、私には適切に見えます! http://www.manugarg.com/2007/04/real-tailing-in-python.html