转换POSIX->;WIN路径,在Cygwin Python中,不调用cygpath
我使用Python的Cygwin版本中运行的Python脚本来创建发布给本机Windows实用程序(不支持Cygwin)的命令。这需要在发出命令之前将路径参数从POSIX转换为WIN表单 调用cygpath实用程序是最好的方法,因为它使用Cygwin来完成它要做的事情,但也有点可怕(而且速度慢) 我已经在运行Python的Cygwin版本了,因此提供了进行转换的代码。似乎应该有一个Cygwin/Python特定的扩展,它直接在Python中为我提供了这个功能的挂钩,而不必启动一个全新的进程。从浏览中,它看起来像是cygpath有一个非平凡的实现,并且没有提供任何库版本转换POSIX->;WIN路径,在Cygwin Python中,不调用cygpath,python,cygwin,Python,Cygwin,我使用Python的Cygwin版本中运行的Python脚本来创建发布给本机Windows实用程序(不支持Cygwin)的命令。这需要在发出命令之前将路径参数从POSIX转换为WIN表单 调用cygpath实用程序是最好的方法,因为它使用Cygwin来完成它要做的事情,但也有点可怕(而且速度慢) 我已经在运行Python的Cygwin版本了,因此提供了进行转换的代码。似乎应该有一个Cygwin/Python特定的扩展,它直接在Python中为我提供了这个功能的挂钩,而不必启动一个全新的进程。从浏
cygpath确实支持使用
-f
选项从文件中获取输入(或者使用-f-
从stdin获取输入),并且可以获取多个路径,每次输出一个转换后的路径,因此您可能可以创建一个打开的cygpath实例(使用Python)而不是每次都重新启动cygpath。这可以通过使用ctypes调用Cygwin API实现。下面的代码适用于我–我在Windows 2012上使用的是64位cygwin DLL版本2.5.2,这在Python 2.7.10和Python 3.4.3的cygwin版本上都适用
基本上,我们从cygwin1.dll
调用来执行路径转换。该函数分配包含转换路径的内存缓冲区(使用malloc
)。因此,我们需要使用free
fromcygwin1.dll
来释放它分配的缓冲区
请注意,下面的xunicode
是一个穷人的替代品(Python 2/3兼容性库);如果您需要同时支持Python2和Python3,那么最好的答案是six,但我希望我的示例不依赖于任何非捆绑模块,这就是我这样做的原因
from ctypes import cdll, c_void_p, c_int32, cast, c_char_p, c_wchar_p
from sys import version_info
xunicode = str if version_info[0] > 2 else eval("unicode")
# If running under Cygwin Python, just use DLL name
# If running under non-Cygwin Windows Python, use full path to cygwin1.dll
# Note Python and cygwin1.dll must match bitness (i.e. 32-bit Python must
# use 32-bit cygwin1.dll, 64-bit Python must use 64-bit cygwin1.dll.)
cygwin = cdll.LoadLibrary("cygwin1.dll")
cygwin_create_path = cygwin.cygwin_create_path
cygwin_create_path.restype = c_void_p
cygwin_create_path.argtypes = [c_int32, c_void_p]
# Initialise the cygwin DLL. This step should only be done if using
# non-Cygwin Python. If you are using Cygwin Python don't do this because
# it has already been done for you.
cygwin_dll_init = cygwin.cygwin_dll_init
cygwin_dll_init.restype = None
cygwin_dll_init.argtypes = []
cygwin_dll_init()
free = cygwin.free
free.restype = None
free.argtypes = [c_void_p]
CCP_POSIX_TO_WIN_A = 0
CCP_POSIX_TO_WIN_W = 1
CCP_WIN_A_TO_POSIX = 2
CCP_WIN_W_TO_POSIX = 3
def win2posix(path):
"""Convert a Windows path to a Cygwin path"""
result = cygwin_create_path(CCP_WIN_W_TO_POSIX,xunicode(path))
if result is None:
raise Exception("cygwin_create_path failed")
value = cast(result,c_char_p).value
free(result)
return value
def posix2win(path):
"""Convert a Cygwin path to a Windows path"""
result = cygwin_create_path(CCP_POSIX_TO_WIN_W,str(path))
if result is None:
raise Exception("cygwin_create_path failed")
value = cast(result,c_wchar_p).value
free(result)
return value
# Example, convert LOCALAPPDATA to cygwin path and back
from os import environ
localAppData = environ["LOCALAPPDATA"]
print("Original Win32 path: %s" % localAppData)
localAppData = win2posix(localAppData)
print("As a POSIX path: %s" % localAppData)
localAppData = posix2win(localAppData)
print("Back to a Windows path: %s" % localAppData)
我宁愿编写使用
cygwin
dll的Python助手:
import errno
import ctypes
import enum
import sys
class ccp_what(enum.Enum):
posix_to_win_a = 0 # from is char *posix, to is char *win32
posix_to_win_w = 1 # from is char *posix, to is wchar_t *win32
win_a_to_posix = 2 # from is char *win32, to is char *posix
win_w_to_posix = 3 # from is wchar_t *win32, to is char *posix
convtype_mask = 3
absolute = 0 # Request absolute path (default).
relative = 0x100 # Request to keep path relative.
proc_cygdrive = 0x200 # Request to return /proc/cygdrive path (only with CCP_*_TO_POSIX)
class CygpathError(Exception):
def __init__(self, errno, msg=""):
self.errno = errno
super(Exception, self).__init__(os.strerror(errno))
class Cygpath(object):
bufsize = 512
def __init__(self):
if 'cygwin' not in sys.platform:
raise SystemError('Not running on cygwin')
self._dll = ctypes.cdll.LoadLibrary("cygwin1.dll")
def _cygwin_conv_path(self, what, path, size = None):
if size is None:
size = self.bufsize
out = ctypes.create_string_buffer(size)
ret = self._dll.cygwin_conv_path(what, path, out, size)
if ret < 0:
raise CygpathError(ctypes.get_errno())
return out.value
def posix2win(self, path, relative=False):
out = ctypes.create_string_buffer(self.bufsize)
t = ccp_what.relative.value if relative else ccp_what.absolute.value
what = ccp_what.posix_to_win_a.value | t
return self._cygwin_conv_path(what, path)
def win2posix(self, path, relative=False):
out = ctypes.create_string_buffer(self.bufsize)
t = ccp_what.relative.value if relative else ccp_what.absolute.value
what = ccp_what.win_a_to_posix.value | t
return self._cygwin_conv_path(what, path)
导入错误号
导入ctypes
导入枚举
导入系统
类ccp_what(enum.enum):
posix_to_win_a=0#from is char*posix,to is char*win32
posix_to_win_w=1#from is char*posix,to is wchar_t*win32
win_a_to_posix=2#from is char*win32,to is char*posix
win_w_to_posix=3#从is wchar_t*win32到is char*posix
convtype_mask=3
绝对=0#请求绝对路径(默认)。
相对=0x100#请求保持路径相对。
proc_cygdrive=0x200#请求返回/proc/cygdrive路径(仅在CCP_*_到_POSIX的情况下)
类CygpathError(异常):
def(self,errno,msg=“”):
self.errno=errno
super(异常,self)。\uuuu init\uuuu(os.strerror(errno))
类Cygpath(对象):
bufsize=512
定义初始化(自):
如果sys.platform中没有“cygwin”:
raise SystemError('未在cygwin上运行')
self._dll=ctypes.cdll.LoadLibrary(“cygwin1.dll”)
定义路径(self、what、path、size=None):
如果大小为“无”:
size=self.bufsize
out=ctypes。创建字符串缓冲区(大小)
ret=self.\u dll.cygwin\u conv\u路径(什么、路径、输出、大小)
如果ret<0:
raise CygpathError(ctypes.get\u errno())
返回值
def posix2win(self、path、relative=False):
out=ctypes.create\u string\u buffer(self.bufsize)
t=ccp\u什么相对值如果是相对值,则ccp\u什么绝对值
what=ccp_what.posix_to_win_a.value|t
返回自我。_cygwin_conv_路径(what,path)
def win2posix(self、path、relative=False):
out=ctypes.create\u string\u buffer(self.bufsize)
t=ccp\u什么相对值如果是相对值,则ccp\u什么绝对值
what=ccp_what.win_a_to_posix.value|t
返回自我。_cygwin_conv_路径(what,path)
我最近独立地遇到了这个问题。
我想出的小而快的解决方案如下:
import os
import re
def win_path(path):
match = re.match('(/(cygdrive/)?)(.*)', path)
if not match:
return path.replace('/', '\\')
dirs = match.group(3).split('/')
dirs[0] = f'{dirs[0].upper()}:'
return '\\'.join(dirs)
这适用于cygwin(/cygdrive/…
)和MinGW(/…
)样式的路径(我必须同时支持两者)以及相对路径
l = ['/c/test/path',
'/cygdrive/c/test/path',
'./test/path',
'../test/path',
'C:\Windows\Path',
'.\Windows\Path',
'..\Windows\Path']
for i in l:
print(win_path(i))
产生:
C:\test\path
C:\test\path
.\test\path
..\test\path
C:\Windows\Path
.\Windows\Path
..\Windows\Path
感谢您共享ctypes代码,但我无法使用WinPython-3.5.2和2.7.10使其运行:ctypes
self上的PY2崩溃。\u handle=\u dlopen(self.\u name,mode)
,以及result=cygwin\u create\u path(CCP\u WIN\u W\u TO\u POSIX,xunicode(path))上的PY3错误:异常:访问冲突写入0x0000000000000000000000
。有什么想法吗?@ankostis,我想问题可能出在第cygwin=cdll.LoadLibrary(“cygwin1.dll”)
行。使用Cygwin Python,cygwin1.dll
已经加载到Python进程地址空间中,因此LoadLibrary
很容易找到它。但是,由于WinPython是纯Windows应用程序(没有Cygwin),因此通常不会加载cygwin1.dll
,因此您需要提供cygwin1.dll
的完整路径。另外,如果您的WinPython是64位的,则需要加载64位的cygwin1.dll
;相反,如果您的WinPython是32位的,则需要加载一个32位的cygwin1.dll
。正如您所说,我在另一台机器上进行了尝试,现在得到了以下结果:原始Win32路径:C:\Users\ankostis\AppData\Local 0[main]python 772 D:\Apps\WinPython-64bit-3.5.2.1\python-3.5.2.amd64\python.exe:**致命错误-内部错误:TP_NUM_C_BUFS太小:50 1090[main]python 772 cygwin_异常::open_stackdump文件:将堆栈跟踪转储到python.exe.stackdump@tokoti(cygwin):~/Work/gitdb.git$less python.exe.stackdump
@ankostis,我认为这是因为您需要首先调用cygwin\u dll\u init
。我已经更新了我的答案,以包括该步骤。请注意,我本来不必这样做,因为我使用的是Cygwin Python,而在Cygwin Python中,Cygwin DLL已经初始化。在Cygwin下,您应该能够捕获errno
以获得赌注