Python - Windows - Popen(shlex.split(command), shell=False 导致 OSError: [Errno 2] 没有这样的文件或目录

0 投票
2 回答
1668 浏览
提问于 2025-04-18 07:18

我在运行这段代码时,在OSX系统上运行得很好,但在Windows系统上却出现了错误:

command = "C:\\progra~2\\itms\\iTMSTransporter -m verify -f /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp -u username -p password -o /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt -s provider -v eXtreme"
self.process1 = Popen(shlex.split(command), shell=False, stdin=PIPE)

我在Windows上收到的错误信息是:

WindowsError: [Error 2] The system cannot find the file specified

为什么在Windows上会出现这个错误呢?

2 个回答

1

这可能来得有点晚,但也许对遇到类似问题的其他人有帮助。

在开始之前,我通常在处理Windows路径时使用原始字符串,这样可以方便复制粘贴,因为它可以接受单个反斜杠:

In [0]: "C:\\path\\to\\folder" == r"C:\path\to\folder"
Out[0]: True

正如Gabriel M的回答中提到的,问题可能是因为shlex吞掉了反斜杠。在这个回答中,我想提供一个解决方案,而不是简单地把反斜杠替换成斜杠。

In [1]: import shlex

In [2]: command = r"C:\progra~2\itms\iTMSTransporter -m verify 
   ...: -f /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp -u username -p password
   ...: -o /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt -s provider -v eXtreme"

In [3]: shlex.split(command)[0]
Out[3]: 'C:progra~2itmsiTMSTransporter'

一个简单的保留反斜杠的解决办法是给shlex.split()传递posix=False这个选项:

In [4]: shlex.split(command, posix=False)[0]
Out[4]: 'C:\\progra~2\\itms\\iTMSTransporter'

正如你在其他回答的评论中提到的,替换斜杠并没有解决你的问题,可能你的实际问题出在你传给脚本的-f-o路径上。也许你传递的路径的脚本需要反斜杠,或者希望路径中包含驱动器字母。无论如何,了解你在过去六年中是否找到了解决方案,以及这个解决方案是什么,会很有趣。


进一步的选项

你还可以使用pathlib(这是Python3的一个标准库,用于处理操作系统相关的路径格式),它的Path提供了一个as_posix()方法,可以将路径转换为带有正斜杠的字符串。这会导致你的shlex.split()输出中出现正斜杠。如果你的路径来自一个变量,而不是直接硬编码,这个方法会特别有用(在后者的情况下,你可以直接更改(反)斜杠):

In [5]: from pathlib import Path

In [6]: command = Path(r"C:\progra~2\itms\iTMSTransporter").as_posix() + " -m verify 
   ...: -f /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp -u username -p password
   ...: -o /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt -s provider -v eXtreme"

In [7]: shlex.split(command)[0]
Out[7]: 'C:/progra~2/itms/iTMSTransporter'

另外,下面这个是In [4]的改进版本,应该在任何操作系统上都能工作。在这种情况下,posix是通过os.name来确定的。这同样依赖于使用pathlib进行操作系统相关的路径格式处理。

In [8]: import os

In [9]: command = str(Path(r"C:\progra~2\itms\iTMSTransporter")) + " -m verify 
   ...: -f /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp -u username -p password
   ...: -o /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt -s provider -v eXtreme"

In [10]: shlex.split(command, posix=(os.name == "posix"))[0]
Out[10]: 'C:\\progra~2\\itms\\iTMSTransporter'
2

你的 shlex.split() 会把路径搞坏,因为它会去掉 \ 这个字符。我们来看看:

import shlex
command = "C:\\progra~2\\itms\\iTMSTransporter -m verify -f  Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp -u username -p password -o /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt -s provider -v eXtreme"
print shlex.split(command)

['C:progra~2itmsiTMSTransporter', '-m', 'verify', '-f', '/Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp', '-u', 'username', '-p', 'password', '-o', '/Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt', '-s', 'provider', '-v', 'eXtreme']

你可以看到,执行文件的路径不对了 (C:progra~2itmsiTMSTransporter),所以 Popen 找不到它。

把你的路径分隔符改成 /,这样在 Linux 和 Windows 环境下都能安全使用:

command = "C:/progra~2/itms/iTMSTransporter -m verify -f  Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp -u username -p password -o /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt -s provider -v eXtreme"
print shlex.split(command)

['C:/progra~2/itms/iTMSTransporter', '-m', 'verify', '-f', 'Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp', '-u', 'username', '-p', 'password', '-o', '/Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt', '-s', 'provider', '-v', 'eXtreme']

Popen() 会正确处理这个路径。

撰写回答