69

我的应用程序,我有以下要求: 1. 有一个线程会定期在文件中记录一些日志。日志文件将在一定的时间间隔内翻转。用于保持日志文件较小。2.还有另外一个线程也会定期处理这些日志文件。例如:将日志文件移动到其他地方,解析日志内容以生成一些日志报告。

但是,有一个条件是第二个线程无法处理用于记录日志的日志文件。在代码方面,伪代码类似如下:

#code in second thread to process the log files
for logFile in os.listdir(logFolder):
     if not file_is_open(logFile) or file_is_use(logFile):
          ProcessLogFile(logFile) # move log file to other place, and generate log report....

那么,我如何检查文件是否已经打开或被其他进程使用?我在互联网上做了一些研究。并有一些结果:

try:
   myfile = open(filename, "r+") # or "a+", whatever you need
except IOError:
    print "Could not open file! Please close Excel!"

我试过这段代码,但不管我使用“r+”还是“a+”标志,它都不起作用

try:
   os.remove(filename) # try to remove it directly
except OSError as e:
    if e.errno == errno.ENOENT: # file doesn't exist
        break

这段代码可以工作,但它无法达到我的要求,因为我不想删除文件来检查它是否打开。

4

7 回答 7

56

试图找出文件是否正在被另一个进程使用的问题是可能出现竞争条件。你可以检查一个文件,确定它没有被使用,然后在你打开它之前,另一个进程(或线程)跳进来抓住它(甚至删除它)。

好的,假设您决定接受这种可能性并希望它不会发生。检查其他进程正在使用的文件取决于操作系统。

在 Linux 上这相当容易,只需遍历 /proc 中的 PID。这是一个生成器,它迭代用于特定 PID 的文件:

def iterate_fds(pid):
    dir = '/proc/'+str(pid)+'/fd'
    if not os.access(dir,os.R_OK|os.X_OK): return

    for fds in os.listdir(dir):
        for fd in fds:
            full_name = os.path.join(dir, fd)
            try:
                file = os.readlink(full_name)
                if file == '/dev/null' or \
                  re.match(r'pipe:\[\d+\]',file) or \
                  re.match(r'socket:\[\d+\]',file):
                    file = None
            except OSError as err:
                if err.errno == 2:     
                    file = None
                else:
                    raise(err)

            yield (fd,file)

在 Windows 上,它不是那么简单,API 没有发布。有一个 sysinternals 工具 ( handle.exe) 可以使用,但我推荐 PyPi 模块psutil,它是可移植的(即它也可以在 Linux 上运行,并且可能在其他操作系统上运行):

import psutil

for proc in psutil.process_iter():
    try:
        # this returns the list of opened files by the current process
        flist = proc.open_files()
        if flist:
            print(proc.pid,proc.name)
            for nt in flist:
                print("\t",nt.path)

    # This catches a race condition where a process ends
    # before we can examine its files    
    except psutil.NoSuchProcess as err:
        print("****",err) 
于 2012-06-20T08:23:44.207 回答
36

我喜欢Daniel 的回答,但对于Windows 用户,我意识到将文件重命名为它已有的名称更安全、更简单。这解决了评论中对他的回答提出的问题。这是代码:

import os

f = 'C:/test.xlsx'
if os.path.exists(f):
    try:
        os.rename(f, f)
        print 'Access on file "' + f +'" is available!'
    except OSError as e:
        print 'Access-error on file "' + f + '"! \n' + str(e)
于 2016-05-16T14:17:04.727 回答
30

您可以使用下一个函数检查文件是否具有句柄(请记住将完整路径传递给该文件):

import psutil

def has_handle(fpath):
    for proc in psutil.process_iter():
        try:
            for item in proc.open_files():
                if fpath == item.path:
                    return True
        except Exception:
            pass

    return False
于 2017-06-18T13:07:19.483 回答
8

我知道我迟到了,但我也遇到了这个问题,我使用lsof命令来解决它(我认为这是上述方法的新方法)。使用lsof我们基本上可以检查正在使用这个特定文件的进程。这是我的做法:

from subprocess import check_output,Popen, PIPE
try:
   lsout=Popen(['lsof',filename],stdout=PIPE, shell=False)
   check_output(["grep",filename], stdin=lsout.stdout, shell=False)
except:
   #check_output will throw an exception here if it won't find any process using that file

只需在 except 部分编写您的日志处理代码,您就可以开始了。

于 2017-08-18T19:56:58.483 回答
3

您可以使用inotify监视文件系统中的活动。您可以观察文件关闭事件,表明发生了翻转。您还应该在文件大小上添加附加条件。确保从第二个线程中过滤掉文件关闭事件。

于 2012-06-20T10:50:36.643 回答
3

而不是使用 os.remove() 您可以在 Windows 上使用以下解决方法:

import os

file = "D:\\temp\\test.pdf"
if os.path.exists(file):
    try:
        os.rename(file,file+"_")
        print "Access on file \"" + str(file) +"\" is available!"
        os.rename(file+"_",file)
    except OSError as e:
        message = "Access-error on file \"" + str(file) + "\"!!! \n" + str(e)
        print message
于 2014-06-18T13:02:25.103 回答
1

上面的答案之一的稍微完善的版本。

from pathlib import Path


def is_file_in_use(file_path):
    path = Path(file_path)
    
    if not path.exists():
        raise FileNotFoundError
    
    try:
        path.rename(path)
    except PermissionError:
        return True
    else:
        return False
于 2021-03-12T11:18:46.593 回答