检查Python中是否未打开文件(其他进程未使用)

zen*_*wke 43 python

我的申请,我有以下要求: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....
Run Code Online (Sandbox Code Playgroud)

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

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

我尝试了这段代码,但无论我使用"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
Run Code Online (Sandbox Code Playgroud)

此代码可以工作,但它无法达到我的请求,因为我不想删除该文件以检查它是否已打开.

cda*_*rke 40

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

好吧,假设你决定忍受这种可能性并希望它不会发生.检查其他进程使用的文件是依赖于操作系统的.

在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)
Run Code Online (Sandbox Code Playgroud)

在Windows上它不是那么简单,API不会发布.有一个handle.exe可以使用的sysinternals工具(),但我推荐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) 
Run Code Online (Sandbox Code Playgroud)

  • 非常好,但在你的Linux示例中,我建议使用errno.ENOENT而不是值2. (2认同)
  • 这对我有用,但我还必须捕捉psutil.AccessDenied异常 (2认同)

Mar*_*ese 20

我喜欢丹尼尔的答案,但我意识到将文件重命名为已有的名称会更安全,更简单.这解决了他在答案中提出的问题.我会在评论中说这个,但我没有分数.这是代码:

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)
Run Code Online (Sandbox Code Playgroud)

  • 我很确定这不适用于非Windows操作系统(我的Linux系统很容易让我重命名我在另一个进程中打开的数据库文件). (3认同)

Tav*_*avy 17

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

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
Run Code Online (Sandbox Code Playgroud)

  • 非常好的解决方案。这是跨平台的吗?我在linux上运行得很好,那么windows上呢? (2认同)
  • @DennisLi 同样的事情也发生在我身上。Vim 似乎使用保存在 `~/.config` 的 vim 目录中的 `.swp` 文件。原始文件不是由 Vim 打开(好吧,在我的例子中是 Neovim)。 (2认同)

Ami*_*t G 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
Run Code Online (Sandbox Code Playgroud)

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