Получение владельца файла для Windows с помощью Python быстрее

Короче говоря: я создаю базу данных, которая включает все котировки, когда-либо сделанные в нашей компании. Следить за конкретным расширением файла: *.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.stat() - просто не работает в Windows). Я пробовал другой способ, описанный здесь: как найти владельца файла или каталога в python Автор Paal Pedersen, но это даже медленнее, чем при использовании Windows Api.

Я использую os.walk() для поиска файлов на сервере. У меня нет точного местоположения файлов, они могут быть в любой папке (поэтому я просто просматриваю каждый файл во всех папках/подпапках и смотрю, является ли он файлом *.prc). Один предложил многопроцессорность - большое спасибо :) Я попытаюсь оптимизировать весь код, но мой вопрос остается в силе - есть ли более быстрый/лучший способ найти владельца файла в ОС Windows?

@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")

person Vlad    schedule 17.02.2021    source источник
comment
Отвечает ли это на ваш вопрос? Как распараллелить простой цикл Python?   -  person Peter Badida    schedule 17.02.2021


Ответы (2)


В таких случаях полезно запускать код через профайлер:

> 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)]

Между использованием этой версии функции и выводом данных в файл вместо относительно медленной консоли время сократилось с 252 секунд до 5 секунд в тестовой папке на моей локальной машине с 60 000 файлов.

person Anon Coward    schedule 19.02.2021
comment
Спасибо за ваше время! Я на самом деле сократил время вдвое с вашим предложением :) К вашему удивлению об открытии и закрытии файла ... вы знаете, я новичок в python и в программировании, все еще учусь и еще многому предстоит научиться. Но однажды я буду там! Хороших выходных. - person Vlad; 20.02.2021

попробуйте это, возможно, он вернет список всех владельцев в каталоге и подкаталогах.

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}}" 
person theCreator    schedule 18.02.2021
comment
1. выполнение занимает в 5-6 раз больше времени 2. не работает :) В результате получаю окно и целое число. - person Vlad; 19.02.2021