2012-06-04 58 views
10

我使用Python腳本,在Cygwin的Python版本中運行,以創建發佈到本機Windows實用程序(不是Cygwin感知的)的命令。這需要在發出命令之前將路徑參數從POSIX轉換爲WIN格式。在Cygwin Python中轉換POSIX-> WIN路徑,不使用調用cygpath

調用cygpath實用程序是最好的方法,因爲它使用Cygwin做它在做的事情,但它也有點可怕(而且很慢)。

我已經在運行Python的Cygwin構建 - 所以執行轉換的代碼就存在了。似乎應該有一個Cygwin/Python特定的擴展,直接在Python中爲我提供了這個功能的鉤子,而無需啓動一個全新的進程。

回答

1

從瀏覽cygpath source,它看起來像cygpath有一個非平凡的實現,並沒有任何庫版本可用。

cygpath不支持使用-f選項以它的輸入從文件(或標準輸入,使用-f -),並可以採取多條路徑,每次吐出一個轉換的路徑,所以你也許可以打造開放式的單一cygpath實例(使用Python的subprocess.Popen),而不是每次重新啓動cygpath。

3

這可以通過使用ctypes調用Cygwin API來實現。下面的代碼適用於我 - 我在Windows 2012上使用64位cygwin DLL版本2.5.2,這適用於Python 2.7.10和Python 3.4.3的Cygwin版本。

基本上我們稱cygwin_create_pathcygwin1.dll執行路徑轉換。該函數分配一個包含已轉換路徑的內存緩衝區(使用malloc)。那麼我們需要使用cygwin1.dll中的free來釋放它分配的緩衝區。

請注意,下面的xunicodesix(一個Python 2/3兼容庫)的替代品。如果你需要同時支持Python 2和Python 3,六個是更好的答案,但我希望我的示例不受任何非捆綁模塊的依賴,這就是爲什麼我這樣做的原因。

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) 
+0

感謝您分享的ctypes的代碼,但我不能使它與WinPython-3.5.2和2.7上運行。 10:上ctypes的''self._handle = _dlopen(self._name,模式)'' PY2崩潰,並且 PY3上''結果= cygwin_create_path(CCP_WIN_W_TO_POSIX,xunicode(路徑)) OSERROR:異常:訪問衝突寫入0x0000000000000000''。有任何想法嗎? – ankostis

+0

@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'。 –

+0

我嘗試在另一臺機器上,因爲你說,現在我得到這個: '原始Win32路徑:C:\ Users \ ankostis \ AppData \ Local 0 [main] python 772 D:\ Apps \ WinPython-64bit- 3.5.2.1 \蟒蛇,3.5.2.amd64 \蟒蛇。exe:***致命錯誤 - 內部錯誤:TP_NUM_C_BUFS太小:50 1090 [main] python 772 cygwin_exception :: open_stackdumpfile:將堆棧跟蹤轉儲到python.exe.stackdump @tokoti(cygwin):〜/ Work/gitdb。 git $ less python.exe.stackdump' – ankostis

0

我寧願編寫使用cygwin DLL這條巨蟒幫手:

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) 
相關問題