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ổ?
Trả lời
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 đó.
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
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
Tôi đã thêm một ví dụ về liên kết thư mục, sử dụng unicode –
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
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
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 +.
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
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 đó. –
Ồ 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
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
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). –
os.symlink hoạt động trên Python 3.3 bằng Windows 8.1 với hệ thống tệp NTFS.
đâ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)
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
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)
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
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 đó. –
Tại sao bạn cần chức năng đó? –