Czy ktoś wie o sposobie tworzenia/odczytu dowiązań symbolicznych między wersjami Win32 z Pythona? Najlepiej byłoby, gdyby minimalna ilość kodu była specyficzna dla platformy, ponieważ potrzebuję, aby moja aplikacja była platformą krzyżową.Dowiązania symboliczne w oknach?
Odpowiedz
Problem jest, jak wyjaśniono np. here, że obsługa Windows dla funkcji dowiązań symbolicznych jest różna w różnych wersjach systemu Windows, więc np. w Vista (z dużą ilością pracy) można uzyskać więcej funkcji niż w XP lub 2000 (nic AFAIK w innych wersjach Win32). Możesz też zamiast tego mieć skróty, które oczywiście mają swój własny zestaw ograniczeń i nie są "tak naprawdę" odpowiednikiem dowiązań symbolicznych Uniksa. Musisz więc dokładnie określić, jakie funkcje potrzebujesz, ile z nich chcesz poświęcić na ołtarzu operacji "cross-win32" itd. - W ten sposób możemy opracować, jak wdrożyć kompromis, który wybrałeś w kategoriach z ctypes
lub win32all
połączeń ... to w pewnym sensie najmniejszy z nich.
system plików NTFS ma punkty połączenia, myślę, że można ich używać zamiast tego można użyć modułu API dla Pythona win32 że np
import win32file
win32file.CreateSymbolicLink(fileSrc, fileTarget, 1)
Jeśli nie chcesz polegać na module Win32API, zawsze można użyć ctypes
i bezpośrednio zadzwonić CreateSymbolicLink
API Win32 np
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) mówi Minimalna obsługiwana klient jest Windows Vista
Ponadto: To działa również z katalogów (wskazują, że z trzeciego argumentu). Z obsługą Unicode wygląda to tak:
kdll.CreateSymbolicLinkW(UR"D:\testdirLink", UR"D:\testdir", 1)
również zobaczyć Create NTFS junction point in Python
Nie powinieneś używać 'Windows API' 'A, zamiast tego użyj' ..W' (Unicode) - za każdym razem - nawet w takich przykładach jak to. – sorin
Dodałem przykład linku do katalogu, używając unicode –
Nazwy parametrów przedstawione dla 'win32file.CreateSymbolicLink' są trochę mylące. Dla tych, którzy się zastanawiają, pierwsza to nazwa linku do stworzenia, druga to ścieżka, do której ma prowadzić link. – brianmearns
umieścić następujące do 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
Or jeśli chcesz użyć pywin32, możesz użyć poprzednio podanej metody i przeczytać:
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()
Dostosuj atrybuty w pliku CreateFile do swoich potrzeb, ale w normalnej sytuacji powinien działać. Możesz go poprawić.
Należy także dążyć do folderu skrzyżowaniach jeśli używasz montowania zamiast SYMBOLIC_LINK.
Możesz sposób sprawdzić, że
sys.getwindowsversion()[0] >= 6
jeśli umieścisz to w coś ty zwolnienia, ponieważ ta forma dowiązania symbolicznego jest obsługiwane tylko na Vista +. Kod
Wprowadziłem do tego zmianę, ale zostało ono odrzucone, mimo że w rzeczywistości jest obecny błąd. W islink() zamaskowana wartość musi być porównywana z maską, aby uniknąć fałszywych alarmów (zero = false/nonzero = true skrót byłby tylko z maską jednobitową, np. 1024). W przeciwnym razie normalne katalogi są również oznaczone jako "linki". Wiersz powinien brzmieć "if GetFileAttributes (fpath) i REPARSE_FOLDER == REPARSE_FOLDER:" – MartyMacGyver
Jeśli odrzuciłem to, musiałem zrobić to przez pomyłkę i jest mi przykro z tego powodu. (Szczerze mówiąc, nie jestem do końca pewny, gdzie chciałbym zobaczyć prośby o edycję, biorąc pod uwagę, że nigdy nie otrzymałem żadnego z nich tutaj). Poszedłem jednak naprzód i zaktualizowałem Twoją poprawkę, więc dziękuję ci za to. –
O nie, * ty * nie odrzuciłeś niczego ... zrobili to dwaj inni redaktorzy, co było dziwne, ponieważ moja sugerowana edycja po prostu poprawiła błąd. To powiedziawszy, jeszcze raz dziękuję za twój post - to było bardzo pomocne i warte zagłębienia się! :-) – MartyMacGyver
Juntalis męska nie obsługuje Unicode, więc modyfikować go używać ctypes a także uproszczone go przy użyciu struct. Ja również konsultowany kod z 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
Tak, nie zdawałem sobie sprawy w momencie pisania tego, że punkty ponownej analizy były przechowywane jako ciągi znaków Unicode. (Głupia pomyłka, aby być pewnym) Lepiej wykorzystaj powyższy kod lub moduł, który niedawno znalazłem [na githubie] (https://github.com/sid0/ntfs). –
os.symlink działa na Python 3.3 w systemie Windows 8.1 z systemem plików NTFS.
tu jest link zawierający wszystkie sposoby kernel32.dll
http://www.geoffchappell.com/studies/windows/win32/kernel32/api/
użyłem CreateHardLinkA na Windows XP SP3, to działa!
ctypes import jeśli os.path.exists (link_file) os.remove (link_file)
dll = ctypes.windll.LoadLibrary("kernel32.dll")
dll.CreateHardLinkA(link_file, _log_filename, 0)
Dowiązania symboliczne, tak jak zostały wprowadzone w systemie Vista, są implementowane jako punkty ponownej analizy, a tym samym bliżej dowiązań symbolicznych w POSIX. Hardlinki nie mają z tym nic wspólnego. – 0xC0000022L
Używając polecenia mklink w podprocesu utworzenia połączenia.
from subprocess import call
call(['mklink', 'LINK', 'TARGET'], shell=True)
Miałem problemy z innymi rozwiązaniami tutaj - ale to działa dobrze. – Zitrax
Funkcją, której potrzebuję, jest możliwość utworzenia katalogu zawierającego odsyłacze do plików z różnych miejsc w systemie plików, a następnie umożliwienie kodowi Python otworzenia tych plików tak, jakby znajdowały się w tym katalogu. –
Dlaczego potrzebujesz tej funkcji? –