2016-11-01 12 views
1

Pythonとctypesを使用してWindows上でファイルの属性を読み書きする方法はありますか?レコードの場合

  • a手段 'アーカイブ'
  • s手段 'システム' '読み取り専用'
  • h手段 '隠された'
  • r手段 '刃先交換式'
  • i手段

私のcurre Pythonスクリプトからこれらの属性を読み書きするには、subprocessモジュールを使用してattribを呼び出します。

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} 

しかし、ディレクトリは、多くのエントリ(エントリごとに1つのサブプロセスの呼び出しが)含まれている場合、これが遅い実行します。

第三者のモジュールの依存関係が必要ないので、私は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)の完全な一覧を示します。属性をチェックするには、ビット単位のAND(演算子 '&')を使用します。例: 'readonly = attrs&FILE_ATTRIBUTE_READONLY'です。属性を追加するには、ビット単位のOR(すなわち、演算子 '|')を使用します。例: '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モジュール](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。カーネル32.SetFileAttributesW(u "path \\ to \\ file"、attrs):raise ctypes.WinError(ctypes.get_last_error()) '。WinDLL( 'kernel32'、use_last_error = True); Python 2を使用している場合は、パスがUnicode文字列であることを確認してください。 – eryksun

答えて

1

eriksunsの助けを借りて私の質問に私はそれを解決しました。ここに私の質問のコードがありますが、ctypesstatおよび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'をモジュールglobalとして一度だけ読み込みます。 '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 is not'です。 'get'の場合、タプルは' 'I''の値が' attrs&FILE_ATTRIBUTE_NOT_CONTENT_INDEXED'の論理否定であることを示すフラグ値を必要とします。 – eryksun

+1

また、質問では 'attrib'コマンドラインで'/L'を使います。したがって、stat呼び出しは 'dirEntry.stat(follow_symlinks = False)'でなければなりません。 – eryksun

関連する問題