给定文件正在被哪个进程使用?

3
我有一个脚本出现问题,它似乎不定期地无法写入自己的日志文件,并抛出错误“此文件正在被另一个进程使用”。
我知道可以使用try except来处理这个问题,但我想找出为什么会发生这种情况,而不是简单地掩盖它。没有其他东西应该访问那个文件。因此,为了确认错误的来源,我想找出哪个服务正在使用该文件。
在Windows上,有没有一种方法可以检查哪个进程正在使用给定的文件?

1
看一下 psutil 模块。 - acw1668
@acw1668,我已经调查过了,但是似乎没有工具可以找到被其他进程打开的文件。 - SuperBiasedMan
尝试使用psutil.Process(pid).open_files(),其中pid可以从psutil.pids()获取。您还可以使用psutil.process_iter()循环遍历所有进程。 - acw1668
1
在Windows中,似乎open_files()总是返回[]。但在Linux中它是有效的。 - acw1668
由于我的答案被删除,我无法将此问题标记为重复:请查看我在此处给出的对相同问题的答案:https://stackoverflow.com/a/74389649/4340584,在那里我还包括了检索使用给定文件的PID列表的示例代码。 - Robert
2个回答

3
你可以使用微软的handle.exe命令行工具。例如:
import re
import subprocess

_handle_pat = re.compile(r'(.*?)\s+pid:\s+(\d+).*[0-9a-fA-F]+:\s+(.*)')

def open_files(name):
    """return a list of (process_name, pid, filename) tuples for
       open files matching the given name."""
    lines = subprocess.check_output('handle.exe "%s"' % name).splitlines()
    results = (_handle_pat.match(line.decode('mbcs')) for line in lines)
    return [m.groups() for m in results if m]

请注意,这在处理Unicode文件名方面有限制。在Python 2中,子进程将name作为ANSI字符串传递,因为它调用CreateProcessA而不是CreateProcessW。在Python 3中,名称作为Unicode传递。在任何情况下,handle.exe使用有损的ANSI编码编写其输出,因此结果元组中匹配的文件名可能包含最佳匹配字符和"?"替换。

0

请不要删除这个答案,如果我做错了什么,请给我留下评论的机会来纠正它。谢谢!

有一种比遍历所有PID(如评论中建议的那样)更好的方法,它涉及执行Windows API调用以确定给定文件上的所有句柄。请查看下面的代码示例,我已经为另一个问题发布过(但是,由于它没有任何被接受的答案,因此无法标记为重复)。请注意,这仅适用于Windows。

import ctypes
from ctypes import wintypes

path = r"C:\temp\stackoverflow39570207.txt"

# -----------------------------------------------------------------------------
# generic strings and constants
# -----------------------------------------------------------------------------

ntdll = ctypes.WinDLL('ntdll')
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

NTSTATUS = wintypes.LONG

INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
FILE_READ_ATTRIBUTES = 0x80
FILE_SHARE_READ = 1
OPEN_EXISTING = 3
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000

FILE_INFORMATION_CLASS = wintypes.ULONG
FileProcessIdsUsingFileInformation = 47

LPSECURITY_ATTRIBUTES = wintypes.LPVOID
ULONG_PTR = wintypes.WPARAM


# -----------------------------------------------------------------------------
# create handle on concerned file with dwDesiredAccess == FILE_READ_ATTRIBUTES
# -----------------------------------------------------------------------------

kernel32.CreateFileW.restype = wintypes.HANDLE
kernel32.CreateFileW.argtypes = (
    wintypes.LPCWSTR,      # In     lpFileName
    wintypes.DWORD,        # In     dwDesiredAccess
    wintypes.DWORD,        # In     dwShareMode
    LPSECURITY_ATTRIBUTES,  # In_opt lpSecurityAttributes
    wintypes.DWORD,        # In     dwCreationDisposition
    wintypes.DWORD,        # In     dwFlagsAndAttributes
    wintypes.HANDLE)       # In_opt hTemplateFile
hFile = kernel32.CreateFileW(
    path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, None, OPEN_EXISTING,
    FILE_FLAG_BACKUP_SEMANTICS, None)
if hFile == INVALID_HANDLE_VALUE:
    raise ctypes.WinError(ctypes.get_last_error())


# -----------------------------------------------------------------------------
# prepare data types for system call
# -----------------------------------------------------------------------------

class IO_STATUS_BLOCK(ctypes.Structure):
    class _STATUS(ctypes.Union):
        _fields_ = (('Status', NTSTATUS),
                    ('Pointer', wintypes.LPVOID))
    _anonymous_ = '_Status',
    _fields_ = (('_Status', _STATUS),
                ('Information', ULONG_PTR))


iosb = IO_STATUS_BLOCK()


class FILE_PROCESS_IDS_USING_FILE_INFORMATION(ctypes.Structure):
    _fields_ = (('NumberOfProcessIdsInList', wintypes.LARGE_INTEGER),
                ('ProcessIdList', wintypes.LARGE_INTEGER * 64))


info = FILE_PROCESS_IDS_USING_FILE_INFORMATION()

PIO_STATUS_BLOCK = ctypes.POINTER(IO_STATUS_BLOCK)
ntdll.NtQueryInformationFile.restype = NTSTATUS
ntdll.NtQueryInformationFile.argtypes = (
    wintypes.HANDLE,        # In  FileHandle
    PIO_STATUS_BLOCK,       # Out IoStatusBlock
    wintypes.LPVOID,        # Out FileInformation
    wintypes.ULONG,         # In  Length
    FILE_INFORMATION_CLASS)  # In  FileInformationClass

# -----------------------------------------------------------------------------
# system call to retrieve list of PIDs currently using the file
# -----------------------------------------------------------------------------
status = ntdll.NtQueryInformationFile(hFile, ctypes.byref(iosb),
                                      ctypes.byref(info),
                                      ctypes.sizeof(info),
                                      FileProcessIdsUsingFileInformation)
pidList = info.ProcessIdList[0:info.NumberOfProcessIdsInList]
print(pidList)

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