使用 python 子进程 Popen touch 文件

Use python subprocess Popen to touch a file

我是子流程模块的新手,想知道为什么第一个子流程失败而第二个子流程有效。我在 py3.7 和 macOS 上。

>>> from subprocess import PIPE, Popen, STDOUT
>>> Popen(['touch', '/Users/me/fail.txt'], stdout=PIPE, stderr=STDOUT, shell=True)
>>> Popen(['touch /Users/me/ok.txt'], stdout=PIPE, stderr=STDOUT, shell=True)

subprocess.run which is a high-level function, you need to pass the arguments as a list but for Popen 中,这是一个 low-level 函数需要直接命令,因此第一个失败但第二个成功。

根据 docs:

The shell argument (which defaults to False) specifies whether to use the shell as the program to execute. If shell is True, it is recommended to pass args as a string rather than as a sequence.

On POSIX with shell=True, the shell defaults to /bin/sh. If args is a string, the string specifies the command to execute through the shell. This means that the string must be formatted exactly as it would be when typed at the shell prompt. This includes, for example, quoting or backslash escaping filenames with spaces in them. If args is a sequence, the first item specifies the command string, and any additional items will be treated as additional arguments to the shell itself. That is to say, Popen does the equivalent of:

Popen(['/bin/sh', '-c', args[0], args[1], ...])

所以在第一种情况下,列表的第二个元素作为参数传递给 /bin/sh 本身,而不是 touch 命令。所以你基本上是 运行:

user@name ~$ touch

这会产生以下错误:

touch: missing file operand
Try 'touch --help' for more information.

如果您阅读第一个命令的 stdout,您会发现相同的内容:

>>> Popen(['touch', '/Users/me/fail.txt'], stdout=PIPE, stderr=STDOUT, shell=True).stdout.read()
b"touch: missing file operand\nTry 'touch --help' for more information.\n"

所以虽然shell=True,还是传字符串比较好