转换POSIX->;WIN路径,在Cygwin 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中为我提供了这个功能的挂钩,而不必启动一个全新的进程。从浏

我使用Python的Cygwin版本中运行的Python脚本来创建发布给本机Windows实用程序(不支持Cygwin)的命令。这需要在发出命令之前将路径参数从POSIX转换为WIN表单

调用cygpath实用程序是最好的方法,因为它使用Cygwin来完成它要做的事情,但也有点可怕(而且速度慢)

我已经在运行Python的Cygwin版本了,因此提供了进行转换的代码。似乎应该有一个Cygwin/Python特定的扩展,它直接在Python中为我提供了这个功能的挂钩,而不必启动一个全新的进程。

从浏览中,它看起来像是cygpath有一个非平凡的实现,并且没有提供任何库版本


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
from
cygwin1.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
以获得赌注