os.path.join - 我能在Windows和Cygwin间获得一致性吗?
我希望能有一套文件名的组成部分,这样在Windows和Cygwin上都能生成一致且“好看”的文件名。以下是我尝试过的:
Input Windows Cygwin
1 os.path.join('c:', 'foo', 'bar') c:foo\bar c:/foo/bar
2 os.path.join('c:\\', 'foo', 'bar') c:\foo\bar c:\/foo/bar
3 os.path.join('c:/', 'foo', 'bar') c:/foo\bar c:/foo/bar
在Windows上,1不是我想要的,我需要的是绝对路径,而不是相对于当前目录的路径。
2和3都能用,但我认为它们不“好看”,因为在一个平台上混用了正斜杠和反斜杠。如果能避免这种情况,我的错误和日志信息会更容易阅读。
选项4是自己定义一个driveroot
变量,在Windows上等于c:\
,在Cygwin上等于/cygdrive/c
。或者写一个函数,接受一个驱动器字母并返回相同的结果。但我也希望能避免这两个平台之间的特殊情况。
我能否实现我想要的一切(将相同的路径组件连接起来,得到在两个平台上都指向相同绝对路径的结果,并且在任一平台上都不混合路径分隔符)?还是说我必须在某些地方妥协?
[编辑:如果有帮助的话,主要的使用场景是c:\foo
是我在配置时就知道的路径,而bar
(以及后面的组件)是在之后计算出来的。所以我现在的实际代码看起来更像这样:
dir = os.path.join('c:\\', 'foo')
# some time later
os.path.join(dir, 'bar')
这使用了选项2,在Windows上能“好看”地报告文件名,但在Cygwin上报告的文件名就“不好看”了。如果可能的话,我想避免的是:
if this_is_cygwin():
dir = '/cygdrive/c/foo'
else:
dir = 'c:\\foo'
]
2 个回答
在工作中,我需要处理各种各样的Windows环境,有时候是没有Cygwin的,有时候是有Cygwin但用的是非Cygwin的Python,还有Cygwin和Cygwin Python,以及Unix系统。我还没找到一个特别好的办法来应对这些情况,但目前我觉得最不麻烦的方法就是在Windows上使用Cygwin所说的“混合风格”路径。这种路径是Windows风格的,但用的是正斜杠(/)而不是反斜杠(\)。比如说,c:/foo/bar.txt。这样做可以避免很多问题,比如Cygwin的bash终端会把反斜杠当作转义字符。可惜的是,这样做就不能使用Python内置的路径处理工具,得用比较麻烦的方法。
我现在没有装有Python和Cygwin的机器,所以无法测试下面的代码片段。如果有错误请见谅……
#Combine a path
path = '/'.join([ 'c:', 'foo', 'bar'])
#Split it back apart
pieces = path.split('/')
如果不确定,可以尝试调用Cygwin的cygpath工具。在Cygwin中会出现很多奇怪的情况,比如/cygdrive/d/等于d:\,而/cygdrive/d/../../等于c:\cygwin(或者你安装Cygwin的地方)。还要记住,在Unix风格的路径中,反斜杠是用作转义字符的,比如/cygdrive/c/Documents\ and\ Settings。cygpath在处理这些问题上表现得非常出色,如果没有这个工具,通常可以假设你的奇怪情况不存在。
import sys
import subprocess
#Buncha code here ...
#We got somepath from somewhere, and don't know what format it's in.
try:
somepath = subprocess.check_output(['cygpath', '-m', somepath])
except subprocess.CalledProcessError:
#Cheap attempt at coping with the possibility that we're in Windows, but cygpath isn't available.
if sys.platform.startswith('win32'):
mypath = somepath.replace('\\', '/')
else:
mypath = somepath
#Now we can assume somepath is using forward slashes for delimiters.
有些Windows命令如果你给它传递Windows风格的路径会搞混,而有些Cygwin命令如果你传递Windows或混合风格的路径也会搞混。例如,rsync可能会对“c:/foo/bar.txt”感到困惑,因为这看起来像是在指定一个名为“c”的远程计算机上的“/foo/bar.txt”。当你调用这些挑剔的Windows或Cygwin程序时,使用cygpath来让它们开心。如果你在调用一个挑剔的Windows程序,而cygpath又不可用,可以尝试用“winpath = mypath.replace('/', '\')”这种简单的方法。我觉得如果你在转换Unix风格的路径而没有Cygwin的话,这种方法可能会失败,但希望如果你没有Cygwin的话,Windows上也不会有Unix风格的路径……
编辑:David 指出我对 Windows 上“当前目录”的理解是错的。抱歉。不过,可以试试用 os.path.abspath
来获取漂亮的绝对路径,或者如果不需要绝对路径的话,可以用 os.path.normpath
。
在方法 1 中,
os.path.join('c:', 'foo', 'bar')
输出结果 c:foo\bar
是正确的。这意味着在 c:
的 当前目录 下有一个路径 foo\bar
,这和根目录是不一样的。os.path.join
正在按照你的指示操作,它会把 c:
(即 c 盘的 当前 目录)和一些额外的部分组合在一起。
在方法 2 中,
os.path.join(r'c:\', 'foo', 'bar')
Cygwin 的输出是正确的,因为在 Cygwin 中,c:\
并不是一个驱动器的根目录。
你需要的是 os.abspath
函数。这个函数会给你一个你提供的路径的绝对、标准化的版本。
不过:在 Cygwin 和 Windows 上你得到的字符串是不同的。我希望你不是在寻找这个。