使用 Python 更快地获取 Windows 的文件所有者

Getting the owner of the file for Windows with Python faster

长话短说:我正在制作一个数据库,其中包含我们公司做过的所有报价。寻找特定的文件扩展名:*.prc 我想检索的信息之一是文件的所有者。 我正在使用以下代码(仅显示其中的一部分):

import os, time, win32security, subprocess
from threading import Thread
from time import time

def GET_THE_OWNER(FILENAME):
    open (FILENAME, "r").close ()
    sd = win32security.GetFileSecurity (FILENAME, win32security.OWNER_SECURITY_INFORMATION)
    owner_sid = sd.GetSecurityDescriptorOwner ()
    name, domain, type = win32security.LookupAccountSid (None, owner_sid)
    return name

starttime = time()

path = "C:/Users/cbabycv/Documents/Python/0. Quotations/Example"
for root, dirs, files in os.walk(path):
    for file in files:
        if (file.endswith(".prc")):
            #getting data from the file information
            Filename = os.path.join(root,file)
            try:
                Owner = GET_THE_OWNER(Filename)
            except:
                Owner = "Could not get the owner."
            print(Owner)
endtime = time()
print (Owner)
print(endtime-starttime, " sec")

这个过程很慢(尤其是当你必须阅读大约 100.000 个文件时)。我想知道是否有另一种方法可以让它更快? 请注意,我要求 Windows OS 而不是其他所有内容(在这种情况下我不能使用 os.stat() - 根本不适用于 windows) 我已经尝试过这里描述的另一种方式:how to find the owner of a file or directory in python 作者 Paal Pedersen,但它甚至比使用 windows Api

还要慢

我正在使用 os.walk() 查找服务器上的文件。我不知道文件的确切位置,它们可能在任何文件夹中(所以我只是查看所有 folders/subfolders 中的每个文件,看看它是否是 *.prc 文件)。一个建议的多处理 - 非常感谢 :) 我会尝试优化整个代码,但我的问题仍然有效 - 是否有 faster/better 在 Windows OS?

@theCreator 建议使用 powershell。已经尝试过了。这是大约。慢 14 倍...

import os, subprocess
from pathlib import Path
from time import time

starttime = time()
def GET_THE_OWNER(cmd):
    startupinfo = subprocess.STARTUPINFO()
    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    completed = subprocess.run(["powershell.exe", "-Command", "Get-Acl ", cmd, " | Select-Object Owner"], capture_output=True, startupinfo=startupinfo)
    return completed

path = Path('C:/Users/cbabycv/Documents/Python/0. Quotations/Example')
for root, dirs, files in os.walk(path):
    for file in files:
        if (file.endswith(".prc")):
            #getting data from the file information
            Filename = os.path.join(root,file)
            Filename = "\"" + Filename +"\""
            Owner = GET_THE_OWNER(Filename)
            if Owner.returncode != 0:
                print("An error occured: %s", Owner.stderr)
            else:
                print(Owner.stdout)

endtime = time()
print(endtime-starttime, " sec")

尽可能尝试这个,它将return目录和子目录中所有所有者的列表。

import subprocess

mydirPath = "C:\pathTo\SomeStuff\"
name = subprocess.call(["C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe",  "Get-ChildItem "+ mydirPath +" -Force -Recurse | select @{Name=\"Owner\";Expression={(Get-ACL $_.Fullname).Owner}}" 

在这种情况下,通过分析器 运行 代码很有用:

> python3 -m cProfile -s cumtime owners.py
1.251999855041504  sec
         163705 function calls (158824 primitive calls) in 1.263 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      5/1    0.000    0.000    1.263    1.263 {built-in method builtins.exec}
        1    0.019    0.019    1.263    1.263 owners.py:1(<module>)
     4999    0.024    0.000    1.058    0.000 owners.py:6(GET_THE_OWNER)
     4999    0.423    0.000    0.423    0.000 {built-in method win32security.LookupAccountSid}
     4999    0.264    0.000    0.280    0.000 {built-in method io.open}
     4999    0.262    0.000    0.262    0.000 {built-in method win32security.GetFileSecurity}
 5778/938    0.011    0.000    0.130    0.000 os.py:280(walk)
...

这里有一些是没办法的,但是LookupAccountSid和io.open的调用是有帮助的。 SID 不会改变,与文件列表相比,您可以使用的 SID 列表无疑是相当小的。我实际上不确定您为什么要打开文件并关闭它,但仅此一项就需要相当长的时间:

_owner_sid_cache = {}
def GET_THE_OWNER(FILENAME):
    # open (FILENAME, "r").close ()
    sd = win32security.GetFileSecurity (FILENAME, win32security.OWNER_SECURITY_INFORMATION)
    owner_sid = sd.GetSecurityDescriptorOwner ()
    if str(owner_sid) not in _owner_sid_cache:
        name, _domain, _type = win32security.LookupAccountSid (None, owner_sid)
        _owner_sid_cache[str(owner_sid)] = name
    return _owner_sid_cache[str(owner_sid)]

在使用此版本的函数并将数据输出到文件而不是相对较慢的控制台之间,在我的本地计算机上包含 60,000 个文件的测试文件夹上,时间从 252 秒减少到 5 秒。