2016-11-01 8 views
1

Для записей:Как читать или записывать атрибуты файла s h i i в Windows с использованием Python и ctypes?

  • a означает 'хранения с'
  • s означает 'система'
  • h означает 'скрытый'
  • r означает 'только для чтения'
  • i означает 'индексируемой'

My curre nt для чтения/записи этих атрибутов из сценариев Python является вызов attrib с использованием модуля subprocess.

код Python:

import os, subprocess 

def attrib(path, a=None, s=None, h=None, r=None, i=None): 
    attrs=[] 
    if r==True: attrs.append('+R') 
    elif r==False: attrs.append('-R') 
    if a==True: attrs.append('+A') 
    elif a==False: attrs.append('-A') 
    if s==True: attrs.append('+S') 
    elif s==False: attrs.append('-S') 
    if h==True: attrs.append('+H') 
    elif h==False: attrs.append('-H') 
    if i==True: attrs.append('+I') 
    elif i==False: attrs.append('-I') 

    if attrs: # write attributes 
     cmd = attrs 
     cmd.insert(0,'attrib') 
     cmd.append(path) 
     cmd.append('/L') 
     return subprocess.call(cmd, shell=False) 

    else: # just read attributes 
     output = subprocess.check_output(
      ['attrib', path, '/L'], 
      shell=False, universal_newlines=True 
     )[:9] 
     attrs = {'A':False, 'S':False, 'H':False, 'R':False, 'I':False} 
     for char in output: 
      if char in attrs: 
       attrs[char] = True 
     return attrs 

path = 'C:\\test\\' 
for thing in os.listdir(path): 
    print(thing, str(attrib(os.path.join(path,thing)))) 

Выход:

archivable.txt {'A': True, 'I': False, 'S': False, 'H': False, 'R': False} 
hidden.txt {'A': True, 'I': False, 'S': False, 'H': True, 'R': False} 
normal.txt {'A': True, 'I': False, 'S': False, 'H': False, 'R': False} 
readonly.txt {'A': True, 'I': False, 'S': False, 'H': False, 'R': True} 
system.txt {'A': True, 'I': False, 'S': True, 'H': False, 'R': False} 

Но это работает медленно, когда каталог содержит много записей (один подпроцесс вызова для каждой записи).

Я не хочу использовать модуль win32api, потому что мне не нужны зависимости стороннего модуля. Также им любопытно, как это сделать с ctypes.

Я наткнулся на Hide Folders/ File with Python [closed], Set "hide" attribute on folders in windows OS? и Python: Windows System File, но это мне непонятно. Особенно я не понимаю, что такое 0x4 es 0x02 es. Вы можете это объяснить? Можете ли вы привести конкретный пример кода?

+1

атрибуты файла сохраняются в виде битовой карты в 32-битного числа, при этом каждый бит соответствует атрибуту. Бит 0 равен '2 ** 0 == 1'. Бит 1 - '2 ** 1 == 2' и т. Д. Ниже приведен полный список [константы атрибутов файла] (https://msdn.microsoft.com/en-us/library/gg258117). Для проверки атрибута используйте побитовое И (т. Е. Оператор '&'). Например: 'readonly = attrs & FILE_ATTRIBUTE_READONLY'. Чтобы добавить атрибут, используйте побитовое ИЛИ (то есть оператор '|'). Например: 'attrs | = FILE_ATTRIBUTE_READONLY'. – eryksun

+1

Для чтения Python 3.5+ 'stat' имеет [' st_file_attributes'] (https://docs.python.org/3/library/os.html#os.stat_result.st_file_attributes), а константы атрибутов определены в [stat module] (https://docs.python.org/3/library/stat.html#stat.FILE_ATTRIBUTE_ARCHIVE). Самый быстрый способ ставить все файлы в каталоге в 3.5 - использовать ['os.scandir'] (https://docs.python.org/3/library/os.html#os.scandir). – eryksun

+1

Для записи атрибутов файлов вам нужно будет использовать ctypes для вызова ['SetFileAttributesW'] (https://msdn.microsoft.com/en-us/library/aa365535). Например: 'kernel32 = ctypes.WinDLL ('kernel32', use_last_error = True), '' if not kernel32.SetFileAttributesW (u "путь \\ to \\ file", attrs): raise ctypes.WinError (ctypes.get_last_error()) '. Убедитесь, что путь является строкой Unicode, если вы используете Python 2. – eryksun

ответ

1

С помощью комментариев eriksuns к моему вопросу я решил это. Вот код из моего вопроса, но теперь используйте ctypes, stat и os.scandir. Это требует Python 3.5+. Записи в ~ 50 раз быстрее, а чтение - в 900 раз быстрее.

код Python:

from ctypes import WinDLL, WinError, get_last_error 
from stat import \ 
    FILE_ATTRIBUTE_ARCHIVE as A, FILE_ATTRIBUTE_SYSTEM as S, \ 
    FILE_ATTRIBUTE_HIDDEN as H, FILE_ATTRIBUTE_READONLY as R, \ 
    FILE_ATTRIBUTE_NOT_CONTENT_INDEXED as I 
from os import scandir, stat 

def example_usage(): 
    path = 'C:\\test\\' 
    # https://docs.python.org/3/library/ctypes.html#ctypes.WinDLL 
    kernel32 = WinDLL('kernel32', use_last_error=True) 

    print('\njust read the ashri attributes:') 
    # https://docs.python.org/3/library/os.html#os.DirEntry 
    for entry in scandir(path): 
     a,s,h,r,i = myattrib(kernel32, entry) 
     print(entry.path, a,s,h,r,i) 

    print("\nset the readonly attribute on each entry:") 
    for entry in scandir(path): 
     a,s,h,r,i = myattrib(kernel32, entry, r=True) 
     print(entry.path, a,s,h,r,i) 

    print("\nset attributes more than once on the same entry:") 
    for entry in scandir(path): 
     a,s,h,r,i = myattrib(kernel32, entry, a=False, s=False, h=False, r=False, i=False) 
     print(entry.path, a,s,h,r,i) 
     a,s,h,r,i = myattrib(kernel32, entry, update=True, a=True, s=True, h=True, r=True, i=True) 
     print(entry.path, a,s,h,r,i) 

# Use update=True when you call this function a second time on the same DirEntry object. 
def myattrib(kernel32, entry, update=False, a=None, s=None, h=None, r=None, i=None): 

    # get the file attributes as an integer. 
    if not update: # faster 
     attrs = entry.stat(follow_symlinks=False).st_file_attributes 
    else: # slower but reflects changes 
     # notice that this will raise a WinError Access denied on some entries, 
     # for example C:\System Volume Information\ 
     attrs = stat(entry.path, follow_symlinks=False).st_file_attributes 

    # construct the new attributes 
    newattrs = attrs 
    def set(attr, value): 
     nonlocal newattrs 
     # use '{0:032b}'.format(number) to understand what this does. 
     if value is True: newattrs = newattrs | attr 
     elif value is False: newattrs = newattrs & ~attr 
    set(A, a) 
    set(S, s) 
    set(H, h) 
    set(R, r) 
    set(I, i if i is None else not i) 
    # optional add more attributes here, see 
    # https://docs.python.org/3/library/stat.html#stat.FILE_ATTRIBUTE_ARCHIVE 

    # write the new attributes if they changed 
    if newattrs != attrs: 
     if not kernel32.SetFileAttributesW(entry.path, newattrs): 
      raise WinError(get_last_error()) 

    # return an info tuple 
    return (
     bool(newattrs & A), 
     bool(newattrs & S), 
     bool(newattrs & H), 
     bool(newattrs & R), 
     not bool(newattrs & I) 
    ) 

if __name__ == '__main__': 
    example_usage() 

Выход:

just read the ashri attributes: 
C:\test\hidden.txt True False True False True 
C:\test\normal.txt True False False False True 
C:\test\readonly.txt True False False True True 
C:\test\systemfile.txt True True False False True 

set the readonly attribute on each entry: 
C:\test\hidden.txt True False True True True 
C:\test\normal.txt True False False True True 
C:\test\readonly.txt True False False True True 
C:\test\systemfile.txt True True False True True 

set attributes more than once on the same entry: 
C:\test\hidden.txt False False False False False 
C:\test\hidden.txt True True True True True 
C:\test\normal.txt False False False False False 
C:\test\normal.txt True True True True True 
C:\test\readonly.txt False False False False False 
C:\test\readonly.txt True True True True True 
C:\test\systemfile.txt False False False False False 
C:\test\systemfile.txt True True True True True 
+1

Некоторые предложения. Загрузите 'kernel32' только один раз в качестве глобального модуля. В 'set' замените' attrib^4294967295' на '~ attrib'. В 'get', замените' not not (attrs & what) 'на' bool (attrs & what) '. – eryksun

+1

Код в вопросе имеет 'i = None'. Это соответствует 'FILE_ATTRIBUTE_NOT_CONTENT_INDEXED', но атрибут определен отрицательно. Вы можете установить инвертированное значение, например. 'not_i = i, если i - это None else not i'. Для 'get' кортеж нуждается в значении флага, чтобы указать, что значение' 'I '' является логическим отрицанием' attrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED'. – eryksun

+1

Кроме того, в вопросе вы используете '/ L' в командной строке' attrib'. Таким образом, вызов stat должен быть 'dirEntry.stat (follow_symlinks = False)'. – eryksun