tail -f 在 python 中没有 time.sleep
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1475950/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
tail -f in python with no time.sleep
提问by Tzury Bar Yochay
I need to emulate "tail -f" in python, but I don't want to use time.sleep in the reading loop. I want something more elegant like some kind of blocking read, or select.select with timeout, but python 2.6 "select" documentation specifically says: "it cannot be used on regular files to determine whether a file has grown since it was last read." Any other way? In a few days if no solution is given I will read tail's C source code to try to figure it out. I hope they don't use sleep, hehe Thanks.
我需要在 python 中模拟“tail -f”,但我不想在阅读循环中使用 time.sleep。我想要一些更优雅的东西,比如某种阻塞读取,或者带有超时的 select.select,但是 python 2.6“select”文档特别指出:“它不能用于常规文件来确定自上次读取以来文件是否已经增长。 ” 还有什么办法吗?几天后,如果没有给出解决方案,我将阅读tail的C源代码以试图弄清楚。我希望他们不要使用睡眠,呵呵,谢谢。
MarioR
马里奥
回答by Tzury Bar Yochay
(update) Either use FS monitors tools
(更新)要么使用 FS 监视器工具
Or a single sleep usage (which I would you consider as much more elegant).
或者单次睡眠使用(我认为这更优雅)。
import time
def follow(thefile):
thefile.seek(0,2) # Go to the end of the file
while True:
line = thefile.readline()
if not line:
time.sleep(0.1) # Sleep briefly
continue
yield line
logfile = open("access-log")
loglines = follow(logfile)
for line in loglines:
print line
回答by James Reynolds
To minimize the sleep issues I modified Tzury Bar Yochay's solution and now it polls quickly if there is activity and after a few seconds of no activity it only polls every second.
为了尽量减少睡眠问题,我修改了 Tzury Bar Yochay 的解决方案,现在它会在有活动时快速轮询,在几秒钟没有活动后,它只会每秒轮询一次。
import time
def follow(thefile):
thefile.seek(0,2) # Go to the end of the file
sleep = 0.00001
while True:
line = thefile.readline()
if not line:
time.sleep(sleep) # Sleep briefly
if sleep < 1.0:
sleep += 0.00001
continue
sleep = 0.00001
yield line
logfile = open("/var/log/system.log")
loglines = follow(logfile)
for line in loglines:
print line,
回答by Aaron Digulla
When reading from a file, your only choice is sleep (see the source code). If you read from a pipe, you can simply read since the read will block until there is data ready.
从文件中读取时,您唯一的选择是睡眠(请参阅源代码)。如果您从管道读取,您可以简单地读取,因为读取将阻塞,直到有数据准备好。
The reason for this is that the OS doesn't support the notion "wait for someone to write to a file". Only recently, some filesystems added an API where you can listen for changes made to a file but tail is too old to use this API and it's also not available everywhere.
这样做的原因是操作系统不支持“等待某人写入文件”的概念。直到最近,一些文件系统才添加了一个 API,您可以在其中监听对文件所做的更改,但 tail 太旧而无法使用此 API,而且它也并非随处可用。
回答by mic_e
The simplest C
implementation of tail -f
for Linux is this:
最简单C
的tail -f
Linux实现是这样的:
#include <unistd.h>
#include <sys/inotify.h>
int main() {
int inotify_fd = inotify_init();
inotify_add_watch(inotify_fd, "/tmp/f", IN_MODIFY);
struct inotify_event event;
while (1) {
read(inotify_fd, &event, sizeof(event));
[file has changed; open, stat, read new data]
}
}
This is just a minimal example that's obviously lacking error checking and won't notice when the file is deleted/moved, but it should give a good idea about what the Python implementation should look like.
这只是一个最小的例子,显然缺乏错误检查,并且不会注意到文件何时被删除/移动,但它应该很好地了解 Python 实现应该是什么样子。
Here's a proper Python implementation that uses the built-in ctypes
to talk to inotify
in the way outlined above.
这是一个正确的 Python 实现,它使用内置ctypes
函数以inotify
上述方式进行对话。
""" simple python implementation of tail -f, utilizing inotify. """
import ctypes
from errno import errorcode
import os
from struct import Struct
# constants from <sys/inotify.h>
IN_MODIFY = 2
IN_DELETE_SELF = 1024
IN_MOVE_SELF = 2048
def follow(filename, blocksize=8192):
"""
Monitors the file, and yields bytes objects.
Terminates when the file is deleted or moved.
"""
with INotify() as inotify:
# return when we encounter one of these events.
stop_mask = IN_DELETE_SELF | IN_MOVE_SELF
inotify.add_watch(filename, IN_MODIFY | stop_mask)
# we have returned this many bytes from the file.
filepos = 0
while True:
with open(filename, "rb") as fileobj:
fileobj.seek(filepos)
while True:
data = fileobj.read(blocksize)
if not data:
break
filepos += len(data)
yield data
# wait for next inotify event
_, mask, _, _ = inotify.next_event()
if mask & stop_mask:
break
LIBC = ctypes.CDLL("libc.so.6")
class INotify:
""" Ultra-lightweight inotify class. """
def __init__(self):
self.fd = LIBC.inotify_init()
if self.fd < 0:
raise OSError("could not init inotify: " + errorcode[-self.fd])
self.event_struct = Struct("iIII")
def __enter__(self):
return self
def __exit__(self, exc_type, exc, exc_tb):
self.close()
def close(self):
""" Frees the associated resources. """
os.close(self.fd)
def next_event(self):
"""
Waits for the next event, and returns a tuple of
watch id, mask, cookie, name (bytes).
"""
raw = os.read(self.fd, self.event_struct.size)
watch_id, mask, cookie, name_size = self.event_struct.unpack(raw)
if name_size:
name = os.read(self.fd, name_size)
else:
name = b""
return watch_id, mask, cookie, name
def add_watch(self, filename, mask):
"""
Adds a watch for filename, with the given mask.
Returns the watch id.
"""
if not isinstance(filename, bytes):
raise TypeError("filename must be bytes")
watch_id = LIBC.inotify_add_watch(self.fd, filename, mask)
if watch_id < 0:
raise OSError("could not add watch: " + errorcode[-watch_id])
return watch_id
def main():
""" CLI """
from argparse import ArgumentParser
cli = ArgumentParser()
cli.add_argument("filename")
args = cli.parse_args()
import sys
for data in follow(args.filename.encode()):
sys.stdout.buffer.write(data)
sys.stdout.buffer.flush()
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print("")
Note that there are various inotify
adapters for Python, such as inotify
, pyinotify
and python-inotify
. Those would basically do the work of the INotify
class.
请注意,inotify
Python有多种适配器,例如inotify
、pyinotify
和python-inotify
。那些基本上会完成INotify
班级的工作。
回答by Kixoms
回答by dugres
You can see herehow to do a "tail -f" like using inotify:
你可以在这里看到如何像使用 inotify 一样执行“tail -f”:
This is an exemple[sic] to show how to use the inotify module, it could be very usefull unchanged though.
A Watcher instance let you define callbacks for any event that occur on any file or directory and subdirectories.
The inotify module is from Recipe 576375
这是一个展示如何使用 inotify 模块的示例[原文如此],但它可能非常有用,但无需更改。
Watcher 实例让您可以为发生在任何文件或目录和子目录上的任何事件定义回调。
inotify 模块来自配方 576375
回答by Giampaolo Rodolà
Most implementations I've seen use readlines() / sleep(). A solution based on inotify or similar mightbe faster but consider this:
我见过的大多数实现都使用 readlines() / sleep()。基于 inotify 或类似的解决方案可能会更快,但请考虑这一点:
- once libinotify tells you a file has changed you would end up using readlines() anyway
calling readlines() against a file which hasn't changed, which is what you would end up doing without libinotify, is already a pretty fast operation:
giampaolo@ubuntu:~$ python -m timeit -s "f = open('foo.py', 'r'); f.read()" -c "f.readlines()" 1000000 loops, best of 3: 0.41 usec per loop
- 一旦 libinotify 告诉您文件已更改,无论如何您最终都会使用 readlines()
对未更改的文件调用 readlines() 已经是一个非常快的操作,如果没有 libinotify,您最终会这样做:
giampaolo@ubuntu:~$ python -m timeit -s "f = open('foo.py', 'r'); f.read()" -c "f.readlines()" 1000000 个循环,最好的 3 个:每个循环 0.41 微秒
Having said this, considering that any solution similar to libinotify has portability issues, I might reconsider using readlines() / sleep(). See: http://code.activestate.com/recipes/577968-log-watcher-tail-f-log/
话虽如此,考虑到任何类似于 libinotify 的解决方案都有可移植性问题,我可能会重新考虑使用 readlines() / sleep()。参见:http: //code.activestate.com/recipes/577968-log-watcher-tail-f-log/
回答by Anurag Uniyal
IMO you should use sleep, it works on all platform and code will be simple
IMO 你应该使用 sleep,它适用于所有平台并且代码很简单
Otherwise you can use platform specific APIs which can tell you when file change e.g. on window use FindFirstChangeNotificationon folder and watch for FILE_NOTIFY_CHANGE_LAST_WRITE events
否则,您可以使用特定于平台的 API,它可以告诉您文件何时更改,例如在窗口上使用FindFirstChangeNotification在文件夹上并观察 FILE_NOTIFY_CHANGE_LAST_WRITE 事件
On linux i think you can use i-notify
在 linux 上,我认为您可以使用i-notify
On Mac OSX use FSEvents
在 Mac OSX 上使用FSEvents
回答by u0b34a0f6ae
If you can use GLib on all platforms, you should use glib.io_add_watch
; then you can use a normal GLib mainloop and process events as they happen, without any polling behavior.
如果你可以在所有平台上使用 GLib,你应该使用glib.io_add_watch
; 然后您可以使用普通的 GLib 主循环并在事件发生时处理事件,而无需任何轮询行为。
http://library.gnome.org/devel/pygobject/stable/glib-functions.html#function-glib--io-add-watch
http://library.gnome.org/devel/pygobject/stable/glib-functions.html#function-glib--io-add-watch
回答by alex tingle
Why don't you just use subprocess.call
on tail
itself?
为什么不直接用subprocess.call
在tail
自己身上?
subproces.call(['tail', '-f', filename])
Edit:Fixed to eliminate extra shell process.
编辑:已修复以消除额外的 shell 进程。
Edit2:Fixed to eliminate deprecated os.popen
and thus the need to interpolate parameters, escape espaces and other stuff, and then run a shell process.
Edit2:已修复以消除不推荐使用的情况os.popen
,因此需要插入参数、转义空格和其他内容,然后运行 shell 进程。