在Python中检查打开的文件是否在打开后被删除

19

在Python中,是否可能检查文件是否已被删除或重新创建?

例如,如果您在脚本中使用了open("file"),然后在该文件仍处于打开状态时进行rm file; touch file;操作,那么尽管文件已被删除,脚本仍将保留对旧文件的引用。


1
也许 os.path.exists 在这种情况下可行? - Alex
3
os.path.exists存在的问题是,即使旧文件已被删除,仍可能存在另一个具有相同路径名称的文件。 - user1502906
3个回答

24
你需要为已打开的文件描述符进行fstat操作。
>>> import os
>>> f = open("testdv.py")
>>> os.fstat(f.fileno())
posix.stat_result(st_mode=33188, st_ino=1508053, st_dev=65027L, st_nlink=1, st_uid=1000, st_gid=1000, st_size=1107, st_atime=1349180541, st_mtime=1349180540, st_ctime=1349180540)
>>> os.fstat(f.fileno()).st_nlink
1

好的,这个文件有一个链接,因此在文件系统中有一个名称。现在将其删除:
>>> os.unlink("testdv.py")
>>> os.fstat(f.fileno()).st_nlink
0

没有更多的链接,因此我们有一个“匿名文件”,只有当我们打开它时才保持存活。创建一个同名的新文件对旧文件没有影响:

>>> g = open("testdv.py", "w")
>>> os.fstat(g.fileno()).st_nlink
1
>>> os.fstat(f.fileno()).st_nlink
0

当然,st_nlink有时可能最初为>1,因此检查它是否为零并不完全可靠(尽管在受控环境中,这可能已足够)。相反,您可以通过比较stat结果来验证您最初打开的路径上的文件是否与您拥有文件描述符的文件相同:
>>> os.stat("testdv.py") == os.fstat(f.fileno())
False
>>> os.stat("testdv.py") == os.fstat(g.fileno())
True

(如果您希望这是100%正确的话,那么您应该仅比较stat结果上的st_dev和st_ino字段,因为其他字段,特别是st_atime,在调用之间可能会更改。)

5
是的。使用 os.stat() 函数 来检查文件长度。如果长度为零(或函数返回错误“文件未找到”),则表示有人删除了该文件。
另外,每次需要写入内容时,您也可以打开+写入+关闭文件。缺点是打开文件是一项相当慢的操作,因此如果需要写入大量数据,则不可行。
为什么?因为新文件不是您正在打开的文件。简而言之,Unix 文件系统有两个级别。一个是目录条目(即文件名、文件大小、修改时间、指向数据的指针),第二个级别是文件数据。
当您打开文件时,Unix 使用名称查找文件数据。之后,它仅在第二个级别上操作 - 对目录条目的更改对任何打开的“文件句柄”没有影响。这正是为什么您可以删除目录条目的原因:您的程序没有使用它。
当您使用 os.stat() 时,您不是查看文件数据,而是再次查看目录条目。
从积极的方面来看,这使您可以创建其他程序无法看到但您的程序可以访问的文件:打开文件,删除文件,然后使用它。由于该文件没有目录条目,因此没有其他程序可以访问数据。
从消极的方面来看,您不能轻松解决像您遇到的这样的问题。

2
在Linux上,您可以查看proc/<pid>/fd/...,即使文件已被删除,也可以访问数据。如果您想复制从YouTube下载的视频,这有时会很方便;-) - hochl
@hochl:有趣。注意:要读取进程的fd目录内容,您需要成为该用户或root(权限为dr-x------),因此仍然是安全的。 - Aaron Digulla
@AaronDigulla 我刚做了一个快速测试,使用fstat(因为stat仍然需要文件名)并查看st_nlink(硬链接的数量),我能够实现我想要的功能。我认为当文件被删除时,文件长度不会改变。 - user1502906
检查文件长度是非常不可靠的--如果有人创建了一个相同名称和长度的文件呢?请看我的答案,以获取更可靠的方法。 - Fred Foo
我同意,你的答案考虑到了更多的边角情况。 - Aaron Digulla
@user1502906:在您的情况下,您不应使用“文件”这个术语;它非常令人困惑。要具体说明。打开文件的文件数据不会更改,但stat()应该检查目录条目(即新文件),当您触碰新文件时,新文件数据应该具有长度0。 - Aaron Digulla

3

是的 - 您可以使用inotify设施来检查文件更改和更多信息。还有一个Python绑定可用于此。使用inotify,您可以监视文件或目录以进行文件系统活动。从手册中可以检测到以下事件:

IN_ACCESS         File was accessed (read) (*).
IN_ATTRIB         Metadata changed, e.g., permissions, timestamps, extended attributes, link count (since Linux 2.6.25), UID, GID, etc. (*).
IN_CLOSE_WRITE    File opened for writing was closed (*).
IN_CLOSE_NOWRITE  File not opened for writing was closed (*).
IN_CREATE         File/directory created in watched directory (*).
IN_DELETE         File/directory deleted from watched directory (*).
IN_DELETE_SELF    Watched file/directory was itself deleted.
IN_MODIFY         File was modified (*).
IN_MOVE_SELF      Watched file/directory was itself moved.
IN_MOVED_FROM     File moved out of watched directory (*).
IN_MOVED_TO       File moved into watched directory (*).
IN_OPEN           File was opened (*).

从这里你可以自己谷歌解决方案,但我想你已经有了总体的想法。当然,这可能只适用于Linux,但根据您的问题,我假设您正在使用它(参考rmtouch)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接