Python windows上的符号链接?
有人知道从Python跨win32版本生成/读取符号链接的方法吗?理想情况下,应该有最少的特定于平台的代码,因为我需要我的应用程序跨平台。问题是,正如所解释的,例如,Windows本身对符号链接功能的支持在Windows版本中有所不同,因此,例如,在Vista(需要大量工作)中,您可以获得比XP或2000更多的功能(在其他win32版本上没有问题)。或者您可以使用快捷方式,当然,快捷方式有其自身的限制,并且不是“真正的”相当于Unix符号链接。因此,您必须明确指定您需要什么功能,您愿意在跨win32操作的祭坛上牺牲多少功能,等等——然后,我们可以解决如何实现您在Python windows上的符号链接?,python,winapi,symlink,pywin32,Python,Winapi,Symlink,Pywin32,有人知道从Python跨win32版本生成/读取符号链接的方法吗?理想情况下,应该有最少的特定于平台的代码,因为我需要我的应用程序跨平台。问题是,正如所解释的,例如,Windows本身对符号链接功能的支持在Windows版本中有所不同,因此,例如,在Vista(需要大量工作)中,您可以获得比XP或2000更多的功能(在其他win32版本上没有问题)。或者您可以使用快捷方式,当然,快捷方式有其自身的限制,并且不是“真正的”相当于Unix符号链接。因此,您必须明确指定您需要什么功能,您愿意在跨win
ctypes
或win32all
调用方面选择的折衷方案……这是最起码的从某种意义上讲,NTFS文件系统有连接点,我认为您可以使用它们,您可以使用python win32 API模块来实现这一点,例如
import win32file
win32file.CreateSymbolicLink(fileSrc, fileTarget, 1)
如果您不想依赖win32 API模块,您可以始终使用ctypes
并直接调用CreateSymbolicLink
win32 API,例如
import ctypes
kdll = ctypes.windll.LoadLibrary("kernel32.dll")
kdll.CreateSymbolicLinkA("d:\\test.txt", "d:\\test_link.txt", 0)
MSDN()表示支持的最低客户端是WindowsVista
此外:这也适用于目录(使用第三个参数表示)。使用unicode支持时,它看起来如下所示:
kdll.CreateSymbolicLinkW(UR"D:\testdirLink", UR"D:\testdir", 1)
也看到
我将以下内容放入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
或者,如果要使用pywin32,可以使用前面提到的方法,要读取,请使用:
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()
如果你把它放在你正在发布的东西中,因为这种形式的符号链接只在Vista+上受支持。Juntalis的代码不处理Unicode,所以我修改了它,使用ctypes,并使用struct简化了它。我还参考了
导入操作系统、ctypes、结构
从ctypes导入Windell、wintypes
FSCTL_GET_repasse_POINT=0x900a8
文件\属性\只读=0x0001
文件\u属性\u隐藏=0x0002
文件\属性\目录=0x0010
文件\u属性\u法线=0x0080
文件\属性\重新分析\点=0x0400
一般读取=0x8000000
一般写入=0x40000000
打开\u现有=3
文件读取属性=0x80
文件\标志\打开\重新分析\点=0x00200000
无效的\u句柄\u值=wintypes.HANDLE(-1).VALUE
无效的\u文件\u属性=0xFFFFFFFF
文件\标志\打开\重新分析\点=2097152
文件\u标志\u备份\u语义=33554432
#文件标志打开重新分析点文件标志备份语义
文件标志重分析备份=35651584
GetFileAttributes=windell.kernel32.GetFileAttributesW
_CreateFileW=windell.kernel32.CreateFileW
_DevIoCtl=windell.kernel32.DeviceIoControl
_DevIoCtl.argtypes=[
wintypes.HANDLE,#HANDLE hDevice
wintypes.DWORD,#DWORD dwIoControlCode
wintypes.LPVOID,#LPVOID lpInBuffer
wintypes.DWORD,#DWORD大小
wintypes.LPVOID,#LPVOID lpburuffer
wintypes.DWORD,#DWORD nOutBufferSize
指针(wintypes.DWORD),#LPDWORD lpbytes返回
wintypes.LPVOID]#LPOVERLAPPED LPOVERLAPPED
_DevIoCtl.restype=wintypes.BOOL
def islink(路径):
断言os.path.isdir(path),path
如果GetFileAttributes(路径)&文件属性重分析点:
返回真值
其他:
返回错误
def设备控制(hDevice、ioControlCode、输入、输出):
#设备控制功能
# http://msdn.microsoft.com/en-us/library/aa363216(v=vs.85).aspx
如果输入:
输入大小=长度(输入)
其他:
输入大小=0
如果isinstance(输出,int):
输出=类型。创建字符串缓冲区(输出)
输出大小=透镜(输出)
断言isinstance(输出,ctypes.Array)
bytesReturned=wintypes.DWORD()
状态=_DeviceOctl(HDDevice、ioControlCode、input、,
输入大小,输出,输出大小,字节返回,无)
打印“状态(%d)”%status
如果状态!=0:
返回输出[:bytesReturned.value]
其他:
一无所获
def CreateFile(路径、访问、共享模式、创建、标志):
return _CreateFileW(路径、访问、共享模式、无、创建、标志、无)
SymbolicClinkResparseFormat=“LHHHHHL”
SymbolClinkResparseSize=struct.calcsize(SymbolClinkResparseFormat);
def读取链接(路径):
“”“Windows readlink实现。”“”
#据我所知,如果文件不存在,则不会返回true。
断言islink(路径)
断言类型(路径)=unicode
#根据字符串类型正确打开文件。
hfile=CreateFile(路径,通用\u读取,0,打开\u现有,
文件\u标志\u重新分析\u备份)
#最大重新分析数据缓冲区大小=16384=(16*1024)
缓冲区=设备控制(hfile,FSCTL\u获取\u重新分析\u点,无,16384)
闭合手柄(hfile)
#最小可能长度(假设目标的长度大于0)
如果不是缓冲区或透镜(缓冲区)<9:
一无所获
#解析并返回我们的结果。
#typedef结构\u重新分析\u数据\u缓冲区{
#乌龙重铺标签;
#USHORT-repassedatalength;
#乌肖特保留;
#联合{
#结构{
#USHORT替代物偏移量;
#USHORT替代漆包线;
#USHORT PrintNameOffset;
#USHORT PrintNameLength;
#乌龙旗;
#WCHAR路径缓冲区[1];
#}符号解析缓冲区;
#结构{
#USHORT替代物偏移量;
#USHORT替代漆包线;
#USHORT PrintNameOffset;
#USHORT PrintNameLength;
#WCHAR路径缓冲区[1];
#}mountpointrepassebuffer;
#结构{
#UCHAR-DataBuffer[1];
#}GenericReparseBuffer;
#}dummy名称;
#}重新分析数据缓冲区,*准备数据缓冲区;
#仅句柄符号为ParseBuffer
(标记、数据长度、保留服务器、SubstituteNameOffset、SubstituteNameLength、,
打印名称偏移,打印
sys.getwindowsversion()[0] >= 6
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
dll = ctypes.windll.LoadLibrary("kernel32.dll")
dll.CreateHardLinkA(link_file, _log_filename, 0)
from subprocess import call
call(['mklink', 'LINK', 'TARGET'], shell=True)
import subprocess
subprocess.call(['cmd', '/c', 'mklink', '<path_for_symlink>', '<path_for_file>'])
A required privilege is not held by the client
import win32com.client
import pythoncom
import os
def create_shortcut(original_filepath, shortcut_filepath):
shell = win32com.client.Dispatch("WScript.Shell")
shortcut = shell.CreateShortCut(shortcut_filepath)
shortcut.Targetpath = original_filepath
shortcut.WindowStyle = 7
shortcut.save()
create_shortcut(r'C:\Users\xxx\Desktop\test.jpg',
r'C:\Users\xxx\Desktop\test.lnk')