2009-09-19 12 views
30

Có ai biết cách tạo/đọc các liên kết tượng trưng trên các phiên bản của win32 từ Python không? Lý tưởng nhất là phải có một số lượng tối thiểu mã nền tảng cụ thể, vì tôi cần ứng dụng của tôi là nền tảng chéo.Các liên kết trên cửa sổ?

+3

Chức năng tôi cần là có thể tạo thư mục chứa liên kết đến tệp từ các vị trí khác nhau trong hệ thống tệp và sau đó có mã Python của tôi có thể mở các tệp đó như thể chúng nằm trong thư mục đó. –

+0

Tại sao bạn cần chức năng đó? –

Trả lời

10

Sự cố, như được giải thích, ví dụ: here, sự hỗ trợ của Windows cho chức năng của các liên kết tượng trưng thay đổi trên các bản phát hành Windows, ví dụ: trong Vista (với rất nhiều công việc), bạn có thể nhận được nhiều chức năng hơn trong XP hoặc 2000 (không có gì AFAIK trên các phiên bản win32 khác). Hoặc bạn có thể có các phím tắt thay vào đó, trong đó tất nhiên có bộ hạn chế riêng của họ và không "thực sự" tương đương với các liên kết tượng trưng Unix. Vì vậy, bạn phải chỉ định chính xác những chức năng nào bạn yêu cầu, số lượng bạn sẵn sàng hy sinh trên bàn thờ của hoạt động cross-win32, vv - THÌ, chúng tôi có thể tìm hiểu cách thực hiện thỏa hiệp mà bạn đã chọn theo thuật ngữ của các cuộc gọi ctypes hoặc win32all ... đó là điều ít nhất, theo nghĩa nào đó.

34

Hệ thống tệp NTFS có điểm giao nhau, tôi nghĩ bạn có thể sử dụng chúng, thay vào đó, bạn có thể sử dụng mô-đun API win32 python cho ví dụ:

import win32file 

win32file.CreateSymbolicLink(fileSrc, fileTarget, 1) 

Nếu bạn không muốn dựa vào mô-đun win32api, bạn luôn có thể sử dụng ctypes và trực tiếp gọi CreateSymbolicLink win32 API ví dụ

import ctypes 

kdll = ctypes.windll.LoadLibrary("kernel32.dll") 

kdll.CreateSymbolicLinkA("d:\\test.txt", "d:\\test_link.txt", 0) 

MSDN (http://msdn.microsoft.com/en-us/library/aa363866(VS.85).aspx) nói được hỗ trợ tối thiểu client là Windows Vista

Ngoài: Điều này cũng làm việc với thư mục (chỉ ra rằng với số thứ ba). Với sự hỗ trợ unicode nó trông như thế này:

kdll.CreateSymbolicLinkW(UR"D:\testdirLink", UR"D:\testdir", 1) 

cũng thấy Create NTFS junction point in Python

+11

Bạn không nên sử dụng API '..A' Windows, sử dụng' ..W' (Unicode) thay thế - mọi lúc - ngay cả trong các ví dụ như thế này. – sorin

+0

Tôi đã thêm một ví dụ về liên kết thư mục, sử dụng unicode –

+1

Các tên thông số được hiển thị cho 'win32file.CreateSymbolicLink' có một chút khó hiểu. Đối với những người đang tự hỏi, đầu tiên là tên của liên kết để tạo, thứ hai là đường dẫn mà nó sẽ liên kết đến. – brianmearns

6

tôi đặt sau đây vào Lib/site-packages/sitecustomize.py

import os 

__CSL = None 
def symlink(source, link_name): 
    '''symlink(source, link_name) 
     Creates a symbolic link pointing to source named link_name''' 
    global __CSL 
    if __CSL is None: 
     import ctypes 
     csl = ctypes.windll.kernel32.CreateSymbolicLinkW 
     csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32) 
     csl.restype = ctypes.c_ubyte 
     __CSL = csl 
    flags = 0 
    if source is not None and os.path.isdir(source): 
     flags = 1 
    if __CSL(link_name, source, flags) == 0: 
     raise ctypes.WinError() 

os.symlink = symlink 
12

python ntfslink extension

Hoặc nếu bạn muốn sử dụng pywin32, bạn có thể sử dụng phương pháp đã nêu trước đó và để đọc, sử dụng:

from win32file import * 
from winioctlcon import FSCTL_GET_REPARSE_POINT 

__all__ = ['islink', 'readlink'] 

# Win32file doesn't seem to have this attribute. 
FILE_ATTRIBUTE_REPARSE_POINT = 1024 
# To make things easier. 
REPARSE_FOLDER = (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT) 

# For the parse_reparse_buffer function 
SYMBOLIC_LINK = 'symbolic' 
MOUNTPOINT = 'mountpoint' 
GENERIC = 'generic' 

def islink(fpath): 
    """ Windows islink implementation. """ 
    if GetFileAttributes(fpath) & REPARSE_FOLDER == REPARSE_FOLDER: 
     return True 
    return False 


def parse_reparse_buffer(original, reparse_type=SYMBOLIC_LINK): 
    """ Implementing the below in Python: 

    typedef struct _REPARSE_DATA_BUFFER { 
     ULONG ReparseTag; 
     USHORT ReparseDataLength; 
     USHORT Reserved; 
     union { 
      struct { 
       USHORT SubstituteNameOffset; 
       USHORT SubstituteNameLength; 
       USHORT PrintNameOffset; 
       USHORT PrintNameLength; 
       ULONG Flags; 
       WCHAR PathBuffer[1]; 
      } SymbolicLinkReparseBuffer; 
      struct { 
       USHORT SubstituteNameOffset; 
       USHORT SubstituteNameLength; 
       USHORT PrintNameOffset; 
       USHORT PrintNameLength; 
       WCHAR PathBuffer[1]; 
      } MountPointReparseBuffer; 
      struct { 
       UCHAR DataBuffer[1]; 
      } GenericReparseBuffer; 
     } DUMMYUNIONNAME; 
    } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; 

    """ 
    # Size of our data types 
    SZULONG = 4 # sizeof(ULONG) 
    SZUSHORT = 2 # sizeof(USHORT) 

    # Our structure. 
    # Probably a better way to iterate a dictionary in a particular order, 
    # but I was in a hurry, unfortunately, so I used pkeys. 
    buffer = { 
     'tag' : SZULONG, 
     'data_length' : SZUSHORT, 
     'reserved' : SZUSHORT, 
     SYMBOLIC_LINK : { 
      'substitute_name_offset' : SZUSHORT, 
      'substitute_name_length' : SZUSHORT, 
      'print_name_offset' : SZUSHORT, 
      'print_name_length' : SZUSHORT, 
      'flags' : SZULONG, 
      'buffer' : u'', 
      'pkeys' : [ 
       'substitute_name_offset', 
       'substitute_name_length', 
       'print_name_offset', 
       'print_name_length', 
       'flags', 
      ] 
     }, 
     MOUNTPOINT : { 
      'substitute_name_offset' : SZUSHORT, 
      'substitute_name_length' : SZUSHORT, 
      'print_name_offset' : SZUSHORT, 
      'print_name_length' : SZUSHORT, 
      'buffer' : u'', 
      'pkeys' : [ 
       'substitute_name_offset', 
       'substitute_name_length', 
       'print_name_offset', 
       'print_name_length', 
      ] 
     }, 
     GENERIC : { 
      'pkeys' : [], 
      'buffer': '' 
     } 
    } 

    # Header stuff 
    buffer['tag'] = original[:SZULONG] 
    buffer['data_length'] = original[SZULONG:SZUSHORT] 
    buffer['reserved'] = original[SZULONG+SZUSHORT:SZUSHORT] 
    original = original[8:] 

    # Parsing 
    k = reparse_type 
    for c in buffer[k]['pkeys']: 
     if type(buffer[k][c]) == int: 
      sz = buffer[k][c] 
      bytes = original[:sz] 
      buffer[k][c] = 0 
      for b in bytes: 
       n = ord(b) 
       if n: 
        buffer[k][c] += n 
      original = original[sz:] 

    # Using the offset and length's grabbed, we'll set the buffer. 
    buffer[k]['buffer'] = original 
    return buffer 

def readlink(fpath): 
    """ Windows readlink implementation. """ 
    # This wouldn't return true if the file didn't exist, as far as I know. 
    if not islink(fpath): 
     return None 

    # Open the file correctly depending on the string type. 
    handle = CreateFileW(fpath, GENERIC_READ, 0, None, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, 0) \ 
       if type(fpath) == unicode else \ 
      CreateFile(fpath, GENERIC_READ, 0, None, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, 0) 

    # MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384 = (16*1024) 
    buffer = DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, None, 16*1024) 
    # Above will return an ugly string (byte array), so we'll need to parse it. 

    # But first, we'll close the handle to our file so we're not locking it anymore. 
    CloseHandle(handle) 

    # Minimum possible length (assuming that the length of the target is bigger than 0) 
    if len(buffer) < 9: 
     return None 
    # Parse and return our result. 
    result = parse_reparse_buffer(buffer) 
    offset = result[SYMBOLIC_LINK]['substitute_name_offset'] 
    ending = offset + result[SYMBOLIC_LINK]['substitute_name_length'] 
    rpath = result[SYMBOLIC_LINK]['buffer'][offset:ending].replace('\x00','') 
    if len(rpath) > 4 and rpath[0:4] == '\\??\\': 
     rpath = rpath[4:] 
    return rpath 

def realpath(fpath): 
    from os import path 
    while islink(fpath): 
     rpath = readlink(fpath) 
     if not path.isabs(rpath): 
      rpath = path.abspath(path.join(path.dirname(fpath), rpath)) 
     fpath = rpath 
    return fpath 


def example(): 
    from os import system, unlink 
    system('cmd.exe /c echo Hello World > test.txt') 
    system('mklink test-link.txt test.txt') 
    print 'IsLink: %s' % islink('test-link.txt') 
    print 'ReadLink: %s' % readlink('test-link.txt') 
    print 'RealPath: %s' % realpath('test-link.txt') 
    unlink('test-link.txt') 
    unlink('test.txt') 

if __name__=='__main__': 
    example() 

Điều chỉnh các thuộc tính trong CreateFile theo nhu cầu của bạn, nhưng trong trường hợp bình thường, nó sẽ hoạt động. Cảm thấy tự do để cải thiện nó.

Nó cũng sẽ hoạt động đối với các liên kết thư mục nếu bạn sử dụng MOUNTPOINT thay vì SYMBOLIC_LINK.

Bạn có thể cách để kiểm tra xem

sys.getwindowsversion()[0] >= 6 

nếu bạn đặt này vào một cái gì đó bạn đang phát hành, kể từ khi hình thức liên kết tượng trưng chỉ được hỗ trợ trên Vista +.

+0

Tôi đã gửi một bản chỉnh sửa cho điều này nhưng nó đã bị từ chối mặc dù thực tế có một lỗi. Trong islink(), giá trị đeo mặt nạ phải được so sánh với mặt nạ để tránh dương tính giả (phím tắt zero = false/nonzero = true sẽ chỉ với mặt nạ đơn bit, ví dụ: 1024). Nếu không thì các thư mục thông thường cũng được xác định là "liên kết". Dòng nên đọc "nếu GetFileAttributes (fpath) & REPARSE_FOLDER == REPARSE_FOLDER:" – MartyMacGyver

+0

Nếu tôi từ chối, tôi phải làm như vậy do nhầm lẫn và tôi xin lỗi về điều đó. (Thành thật mà nói, tôi không hoàn toàn chắc chắn nơi tôi thậm chí sẽ đi để xem yêu cầu chỉnh sửa, cho tôi đã không bao giờ nhận được một ở đây). Tôi đã đi trước và cập nhật với sửa chữa của bạn, mặc dù, vì vậy cảm ơn bạn cho điều đó. –

+0

Ồ không, * bạn * đã không từ chối bất cứ điều gì ... hai biên tập viên khác đã làm, điều này thật đặc biệt vì bản chỉnh sửa được đề xuất của tôi chỉ sửa lỗi. Điều đó nói rằng, cảm ơn một lần nữa cho bài viết của bạn - nó đã rất hữu ích và đáng được đào sâu vào! :-) – MartyMacGyver

1

Mã của Juntalis không xử lý Unicode vì vậy tôi đã sửa đổi nó để sử dụng ctypes và cũng đơn giản hóa nó bằng cách sử dụng struct. Tôi cũng đã tham khảo mã từ Using a struct as a function argument with the python ctypes module

import os, ctypes, struct 
from ctypes import windll, wintypes 

FSCTL_GET_REPARSE_POINT = 0x900a8 

FILE_ATTRIBUTE_READONLY  = 0x0001 
FILE_ATTRIBUTE_HIDDEN  = 0x0002 
FILE_ATTRIBUTE_DIRECTORY  = 0x0010 
FILE_ATTRIBUTE_NORMAL  = 0x0080 
FILE_ATTRIBUTE_REPARSE_POINT = 0x0400 


GENERIC_READ = 0x80000000 
GENERIC_WRITE = 0x40000000 
OPEN_EXISTING = 3 
FILE_READ_ATTRIBUTES = 0x80 
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000 
INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value 

INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF 

FILE_FLAG_OPEN_REPARSE_POINT = 2097152 
FILE_FLAG_BACKUP_SEMANTICS = 33554432 
# FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTI 
FILE_FLAG_REPARSE_BACKUP = 35651584 


GetFileAttributes = windll.kernel32.GetFileAttributesW 
_CreateFileW = windll.kernel32.CreateFileW 
_DevIoCtl = windll.kernel32.DeviceIoControl 
_DevIoCtl.argtypes = [ 
    wintypes.HANDLE, #HANDLE hDevice 
    wintypes.DWORD, #DWORD dwIoControlCode 
    wintypes.LPVOID, #LPVOID lpInBuffer 
    wintypes.DWORD, #DWORD nInBufferSize 
    wintypes.LPVOID, #LPVOID lpOutBuffer 
    wintypes.DWORD, #DWORD nOutBufferSize 
    ctypes.POINTER(wintypes.DWORD), #LPDWORD lpBytesReturned 
    wintypes.LPVOID] #LPOVERLAPPED lpOverlapped 
_DevIoCtl.restype = wintypes.BOOL 


def islink(path): 
    assert os.path.isdir(path), path 
    if GetFileAttributes(path) & FILE_ATTRIBUTE_REPARSE_POINT: 
     return True 
    else: 
     return False 


def DeviceIoControl(hDevice, ioControlCode, input, output): 
    # DeviceIoControl Function 
    # http://msdn.microsoft.com/en-us/library/aa363216(v=vs.85).aspx 
    if input: 
     input_size = len(input) 
    else: 
     input_size = 0 
    if isinstance(output, int): 
     output = ctypes.create_string_buffer(output) 
    output_size = len(output) 
    assert isinstance(output, ctypes.Array) 
    bytesReturned = wintypes.DWORD() 
    status = _DevIoCtl(hDevice, ioControlCode, input, 
         input_size, output, output_size, bytesReturned, None) 
    print "status(%d)" % status 
    if status != 0: 
     return output[:bytesReturned.value] 
    else: 
     return None 


def CreateFile(path, access, sharemode, creation, flags): 
    return _CreateFileW(path, access, sharemode, None, creation, flags, None) 


SymbolicLinkReparseFormat = "LHHHHHHL" 
SymbolicLinkReparseSize = struct.calcsize(SymbolicLinkReparseFormat); 

def readlink(path): 
    """ Windows readlink implementation. """ 
    # This wouldn't return true if the file didn't exist, as far as I know. 
    assert islink(path) 
    assert type(path) == unicode 

    # Open the file correctly depending on the string type. 
    hfile = CreateFile(path, GENERIC_READ, 0, OPEN_EXISTING, 
         FILE_FLAG_REPARSE_BACKUP) 
    # MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384 = (16*1024) 
    buffer = DeviceIoControl(hfile, FSCTL_GET_REPARSE_POINT, None, 16384) 
    CloseHandle(hfile) 

    # Minimum possible length (assuming length of the target is bigger than 0) 
    if not buffer or len(buffer) < 9: 
     return None 

    # Parse and return our result. 
    # typedef struct _REPARSE_DATA_BUFFER { 
    # ULONG ReparseTag; 
    # USHORT ReparseDataLength; 
    # USHORT Reserved; 
    # union { 
    #  struct { 
    #   USHORT SubstituteNameOffset; 
    #   USHORT SubstituteNameLength; 
    #   USHORT PrintNameOffset; 
    #   USHORT PrintNameLength; 
    #   ULONG Flags; 
    #   WCHAR PathBuffer[1]; 
    #  } SymbolicLinkReparseBuffer; 
    #  struct { 
    #   USHORT SubstituteNameOffset; 
    #   USHORT SubstituteNameLength; 
    #   USHORT PrintNameOffset; 
    #   USHORT PrintNameLength; 
    #   WCHAR PathBuffer[1]; 
    #  } MountPointReparseBuffer; 
    #  struct { 
    #   UCHAR DataBuffer[1]; 
    #  } GenericReparseBuffer; 
    # } DUMMYUNIONNAME; 
    # } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; 

    # Only handle SymbolicLinkReparseBuffer 
    (tag, dataLength, reserver, SubstituteNameOffset, SubstituteNameLength, 
    PrintNameOffset, PrintNameLength, 
    Flags) = struct.unpack(SymbolicLinkReparseFormat, 
          buffer[:SymbolicLinkReparseSize]) 
    print tag, dataLength, reserver, SubstituteNameOffset, SubstituteNameLength 
    start = SubstituteNameOffset + SymbolicLinkReparseSize 
    actualPath = buffer[start : start + SubstituteNameLength].decode("utf-16") 
    # This utf-16 string is null terminated 
    index = actualPath.find(u"\0") 
    assert index > 0 
    if index > 0: 
     actualPath = actualPath[:index] 
    if actualPath.startswith(u"?\\"): 
     return actualPath[2:] 
    else: 
     return actualPath 
+0

Vâng, đã không nhận ra tại thời điểm viết nó rằng các điểm reparse được lưu trữ như là chuỗi unicode. (Stupid sai lầm để chắc chắn) Bạn đang tốt hơn hết bằng cách sử dụng mã trên, hoặc một mô-đun tôi chỉ vừa mới tìm thấy [trên github] (https://github.com/sid0/ntfs). –

4

os.symlink hoạt động trên Python 3.3 bằng Windows 8.1 với hệ thống tệp NTFS.

-1

đây là liên kết có chứa tất cả các phương pháp kernel32.dll

http://www.geoffchappell.com/studies/windows/win32/kernel32/api/

tôi đã sử dụng CreateHardLinkA trên Windows xp sp3, nó làm việc!

ctypes nhập khẩu nếu os.path.exists (link_file): os.remove (link_file)

dll = ctypes.windll.LoadLibrary("kernel32.dll") 
dll.CreateHardLinkA(link_file, _log_filename, 0) 
+0

Các liên kết tượng trưng khi chúng được giới thiệu trong Vista được thực hiện dưới dạng các điểm reparse và do đó gần gũi hơn với các liên kết tượng trưng trong POSIX. Hardlinks không có gì để làm với điều đó. – 0xC0000022L

4

Sử dụng lệnh mklink trong tiến trình con tạo liên kết.

from subprocess import call 
call(['mklink', 'LINK', 'TARGET'], shell=True) 
+0

Tôi đã gặp vấn đề với các giải pháp khác ở đây - nhưng điều này hoạt động tốt. – Zitrax