我想在Python中输出tail -F或类似的东西,而不会阻塞或锁定.我发现一些真正的旧代码到做在这里,但我想一定有更好的方法或通过图书馆现在做同样的事情.谁知道一个?
理想情况下,tail.getNewData()每次我想要更多数据时,我都会有类似的东西.
Mat*_*att 60
如果你在Linux上(因为windows不支持调用select on files),你可以使用subprocess模块和select模块.
import time
import subprocess
import select
f = subprocess.Popen(['tail','-F',filename],\
stdout=subprocess.PIPE,stderr=subprocess.PIPE)
p = select.poll()
p.register(f.stdout)
while True:
if p.poll(1):
print f.stdout.readline()
time.sleep(1)
Run Code Online (Sandbox Code Playgroud)
这将轮询输出管道以获取新数据,并在可用时将其打印出来.通常情况下time.sleep(1),print f.stdout.readline()将替换为有用的代码.
您可以使用子进程模块而无需额外的选择模块调用.
import subprocess
f = subprocess.Popen(['tail','-F',filename],\
stdout=subprocess.PIPE,stderr=subprocess.PIPE)
while True:
line = f.stdout.readline()
print line
Run Code Online (Sandbox Code Playgroud)
这也将在添加时打印新行,但它会阻塞,直到尾程序关闭,可能是f.kill().
Pau*_*ine 37
使用sh模块(pip install sh):
from sh import tail
# runs forever
for line in tail("-f", "/var/log/some_log_file.log", _iter=True):
print(line)
Run Code Online (Sandbox Code Playgroud)
[更新]
由于sh.tail with _iter= True是一个生成器,你可以:
import sh
tail = sh.tail("-f", "/var/log/some_log_file.log", _iter=True)
Run Code Online (Sandbox Code Playgroud)
然后你可以用"getNewData":
new_data = tail.next()
Run Code Online (Sandbox Code Playgroud)
请注意,如果尾部缓冲区为空,它将阻塞,直到有更多数据(从您的问题来看,在这种情况下您不清楚您想要做什么).
[更新]
如果你用-F替换-f,这是有效的,但在Python中它会锁定.如果可能的话,我会更有兴趣拥有一个我可以调用以获取新数据的函数. - 伊莱
容器生成器将尾调用置于一个True循环内并捕获最终的I/O异常将具有与-F几乎相同的效果.
def tail_F(some_file):
while True:
try:
for line in sh.tail("-f", some_file, _iter=True):
yield line
except sh.ErrorReturnCode_1:
yield None
Run Code Online (Sandbox Code Playgroud)
如果文件变得不可访问,则生成器将返回None.但是,如果文件可访问,它仍会阻塞,直到有新数据.对于我来说,在这种情况下你想做什么仍然不清楚.
Raymond Hettinger的方法似乎很不错:
def tail_F(some_file):
first_call = True
while True:
try:
with open(some_file) as input:
if first_call:
input.seek(0, 2)
first_call = False
latest_data = input.read()
while True:
if '\n' not in latest_data:
latest_data += input.read()
if '\n' not in latest_data:
yield ''
if not os.path.isfile(some_file):
break
continue
latest_lines = latest_data.split('\n')
if latest_data[-1] != '\n':
latest_data = latest_lines[-1]
else:
latest_data = input.read()
for line in latest_lines[:-1]:
yield line + '\n'
except IOError:
yield ''
Run Code Online (Sandbox Code Playgroud)
如果文件无法访问或没有新数据,此生成器将返回''.
[更新]
倒数第二个答案围绕文件的顶部,只要数据耗尽就会出现. - 伊莱
我认为只要尾部进程结束,第二行就会输出最后十行,-f只要有I/O错误,就会输出.该tail --follow --retry行为与此不远处的我能想到的类Unix环境中的大多数情况下.
也许如果你更新你的问题来解释你的真正目标是什么(你想要模仿尾部的原因),你会得到一个更好的答案.
最后一个答案实际上并没有遵循尾部,只是在运行时读取可用的内容. - 伊莱
当然,tail会默认显示最后10行...你可以使用file.seek将文件指针放在文件的末尾,我会给读者留下一个适当的练习作为练习.
恕我直言,file.read()方法比基于子进程的解决方案更优雅.
nne*_*neo 22
实际上,文件的唯一可移植方式tail -f似乎是从中读取并重试(在a之后sleep)read返回0. tail各种平台上的实用程序使用特定kqueue于平台的技巧(例如在BSD上)来永久地有效地拖尾文件不需要sleep.
因此,tail -f纯粹用Python 实现一个好东西可能不是一个好主意,因为你必须使用最小公分母实现(不依赖于特定于平台的黑客攻击).使用简单的方法在单独的线程中subprocess打开tail -f并迭代这些行,您可以tail在Python中轻松实现非阻塞操作.
示例实现:
import threading, Queue, subprocess
tailq = Queue.Queue(maxsize=10) # buffer at most 100 lines
def tail_forever(fn):
p = subprocess.Popen(["tail", "-f", fn], stdout=subprocess.PIPE)
while 1:
line = p.stdout.readline()
tailq.put(line)
if not line:
break
threading.Thread(target=tail_forever, args=(fn,)).start()
print tailq.get() # blocks
print tailq.get_nowait() # throws Queue.Empty if there are no lines to read
Run Code Online (Sandbox Code Playgroud)
Isa*_*ner 17
将 Ijaz Ahmad Khan 的答案改编为仅在完全写入时产生行(行以换行符结尾)给出了一个没有外部依赖项的 pythonic 解决方案:
def follow(file, sleep_sec=0.1) -> Iterator[str]:
""" Yield each line from a file as they are written.
`sleep_sec` is the time to sleep after empty reads. """
line = ''
while True:
tmp = file.readline()
if tmp is not None:
line += tmp
if line.endswith("\n"):
yield line
line = ''
else if sleep_sec:
time.sleep(sleep_sec)
if __name__ == '__main__':
with open("test.txt", 'r') as file:
for line in follow(file):
print(line, end='')
Run Code Online (Sandbox Code Playgroud)
Eli*_*Eli 12
所以,这已经很晚了,但我又遇到了同样的问题,现在有一个更好的解决方案.只需使用pygtail:
Pygtail读取尚未读取的日志文件行.它甚至可以处理已旋转的日志文件.基于logcheck的logtail2(http://logcheck.org)
Ija*_*han 12
所有使用 tail -f 的答案都不是 Pythonic。
这是pythonic方式:(不使用外部工具或库)
def follow(thefile):
while True:
line = thefile.readline()
if not line or not line.endswith('\n'):
time.sleep(0.1)
continue
yield line
if __name__ == '__main__':
logfile = open("run/foo/access-log","r")
loglines = follow(logfile)
for line in loglines:
print(line, end='')
Run Code Online (Sandbox Code Playgroud)
理想情况下,我有类似tail.getNewData()的东西,每当我想要更多数据时我都可以调用它
我们已经有了一个非常好.只要你想要更多数据, 就可以调用f.read().它将开始读取上一次读取停止的位置,并将读取数据流的末尾:
f = open('somefile.log')
p = 0
while True:
f.seek(p)
latest_data = f.read()
p = f.tell()
if latest_data:
print latest_data
print str(p).center(10).center(80, '=')
Run Code Online (Sandbox Code Playgroud)
要逐行阅读,请使用f.readline().有时,正在读取的文件将以部分读取的行结束.处理该情况,使用f.tell()查找当前文件位置并使用f.seek()将文件指针移回不完整行的开头.有关工作代码,请参阅此ActiveState配方.
您可以使用“ tailer”库:https : //pypi.python.org/pypi/tailer/
它具有获取最后几行的选项:
# Get the last 3 lines of the file
tailer.tail(open('test.txt'), 3)
# ['Line 9', 'Line 10', 'Line 11']
Run Code Online (Sandbox Code Playgroud)
它也可以跟随一个文件:
# Follow the file as it grows
for line in tailer.follow(open('test.txt')):
print line
Run Code Online (Sandbox Code Playgroud)
如果有人想要像尾巴一样的行为,那似乎是一个不错的选择。