如何在读取时检测文件已被截断

Posted

技术标签:

【中文标题】如何在读取时检测文件已被截断【英文标题】:How to detect a file has been truncated while reading 【发布时间】:2019-04-14 21:04:17 【问题描述】:

我正在从一组文件(日志文件)中读取行,因为它们是使用 pyinotify 编写的。

我正在使用 python 本地方法打开和读取文件:

file = open(self.file_path, 'r')
# ... later
line = file.readline()

这通常是稳定的,可以处理被删除和重新创建的文件。 pyinotify 会通知取消链接和后续链接。

但是一些日志文件没有被删除。相反,它们被截断并将新内容写入同一文件的开头。

我无法可靠地检测到何时发生这种情况,因为 pyinotify 只会报告一次写入。我目前得到的唯一证据是 pyinotify 报告写入,readline() 返回一个空字符串。但是,两个后续写入可能会触发相同的行为。

我曾考虑将文件大小与file.tell() 进行比较,但根据文档tell 会产生一个不透明的数字,而且似乎不能相信这是多个字节。

是否有一种简单的方法可以检测文件在读取时被截断?


编辑:

可以使用简单的 shell 命令模拟截断文件:

echo hello > test.log
echo hello >> test.log
# Truncate test.log
echo goodbye > test.log

为了补充这一点,可以使用一个简单的 python 脚本来确认file.tell() 在文件被截断时不会减少:

foo = open('./test.log', 'r')
line = foo.readline()
while line != '':
    print(foo.tell())
    print(line)
    line = foo.readline()

# Put a breakpoint on the following line and 
# truncate the file before it executes
print(foo.tell())

【问题讨论】:

我认为你可以相信这样一个事实,如果tell() 返回的数字比你上次调用它时的数字要小,而且你还没有自己寻找,那么就发生了一些奇怪的事情。如果您可以自信地推断出“奇怪的事情”是文件截断,那么我认为您会很好。 - 这整个想法有点吓到我了。我会竭尽全力不必从其他进程可能会执行任何操作但附加到的文件中读取。 @Steve 没有。至少在 linux 上tell() 在文件被截断时不会移动。在上下文中,这是日志监控。整个想法是从另一个进程正在写入的文件中读取。 好的。我只是在评论我认为我在您的声明中读到的内容……如果要给您一个较小的数字,您可能无法解释tell() 告诉您的内容(哈!)。如果它不会,它不会。正如我所说,我不会有任何经验来编写代码来处理在我阅读文件时可能会删除其内容的文件。祝你好运! 如果文件的每次修改都代表一个版本控制,您的通知程序将代表它运行时的最新版本。您似乎正在尝试考虑运行之间的版本和之后的版本控制。那会准确吗? “一种简单的方法”有点主观。但是,不,我认为没有一种“简单的方法”可以通过查看该文件的内容或归属信息来说明针对该文件发生的操作次数。另外,我认为这不一定是完整性问题,而是时间点和差异概念 识别出截断后你想做什么?你只是想模仿tail -f 的做法吗? 【参考方案1】:

使用os.lseek(file.fileno(),0,os.SEEK_CUR) 获取字节偏移量而不移动文件指针。您不能真正使用常规文件接口来查找,尤其是因为它可能有缓冲文本(不再存在),它还没有对 Python 可见。如果文件不是字节流(例如,Python 3 中的默认 open),它甚至可能位于多字节字符的中间,即使文件立即增长也无法继续回到你的文件偏移量。

【讨论】:

为了清楚起见,您是在建议将 os.lseek(file.fileno(),0,os.SEEK_CUR) 的结果与文件大小 (stat) 进行比较? @PhilipCouling:是的(文件描述符上有fstat,现代Python版本拼写os.stat(file.fileno())没有“f”),或者你可以发出三个 @ 987654327@s 学习偏移量和文件大小,没有其他影响。

以上是关于如何在读取时检测文件已被截断的主要内容,如果未能解决你的问题,请参考以下文章

如何检测 TFS 项目源代码已被修改

如何检测文件是不是被覆盖?

如何检测 Console.In (stdin) 是不是已被重定向?

使用 BufferedInputStream 读取大文件时 Java 文件 IO 被截断

如何在 Python 中检测到 fifo 已被删除

将stdin设置为C中的文件时如何从键盘读取