subprocess.Popen 返回随机结果

1 投票
4 回答
876 浏览
提问于 2025-04-18 06:58

我写了一段简单的代码:

import subprocess
p=subprocess.Popen('mkdir -p ./{a,b,c}', shell=True, stderr=subprocess.STDOUT)
p.wait()

可惜它的表现并不总是如我所愿。比如,当我在我的电脑上运行时,一切正常(ls -l 命令显示我有三个文件夹:a、b 和 c)。但是当我的同事在他的电脑上运行时,他看到的却是一个名为 '{a,b,c}' 的文件夹……我们俩都在用 Python 2.7.3。为什么会这样呢?你会怎么解决这个问题?

我试着自己找答案。根据手册的说法:“args 应该是程序参数的一个序列,或者是一个单独的字符串。默认情况下,如果 args 是一个序列,程序会执行 args 中的第一个项目。如果 args 是一个字符串,那么它的解释会依赖于平台,具体情况在下面描述。有关与默认行为的其他差异,请参见 shell 和可执行参数。除非另有说明,建议将 args 作为一个序列传递。”

所以我试着在 shell 中执行这段代码:

python -c "import subprocess; p=subprocess.Popen(['mkdir', '-p', './{ea,fa,ga}'], shell=True, stderr=subprocess.STDOUT); p.wait()"

结果是:

mkdir: missing operand

我会很感激任何建议

谢谢!

4 个回答

-1

根据我的理解,os.mkdir(path,[mode]) 这个方法在处理跨平台项目时使用起来更安全。

os.mkdir(os.getcwd()/a)

不过,它的使用方式没有用子进程的方法那么优雅。

0

这里有几个问题。

  • 首先:如果你使用的是一系列参数,记得不要把“shell = True”设置为真(这是Popen手册中推荐的做法)。把它设置为假,你会发现你的mkdir命令会被接受。
  • “./{a,b,c}”是我所知道的在bash中的一种特定语法。如果你的同事使用的是其他类型的shell,这个命令可能就不管用,或者表现得不一样。
  • 你应该使用Python的“mkdir”命令,而不是调用shell命令,这样无论服务器、shell还是操作系统是什么,都能正常工作。
0

谢谢大家的回答。

看起来,最好的方法就是直接使用 /bin/sh 的语法。我把我的代码改成了:

'mkdir -p ./a ./b ./c'

就像你们建议的那样。

我没有使用 mkdir() 这个函数,因为我在写一个包含很多系统调用的脚本,我想提供一个优雅的 --dry-run 选项(这样我就可以列出所有的命令)。

问题解决了 - 谢谢你们!

2

这个 ./{a,b,c} 的写法是 bash 的语法,不是所有的命令行都支持这种写法。

文档中提到:

在 Unix 系统中,如果使用 shell=True,那么默认的命令行是 /bin/sh。如果参数是一个字符串,这个字符串就是要通过命令行执行的命令。

所以你的命令只有在 /bin/sh 链接到一个支持这种语法的命令行,比如 bashzsh 时才能正常工作。你的同事可能在用 dash 或其他不支持这种语法的命令行。

你不应该依赖用户的默认命令行。相反,应该写出完整的命令,包含所有的展开内容:

p = subprocess.Popen('mkdir -p ./a ./b ./c', shell=True, stderr=subprocess.STDOUT)

撰写回答