如何使用subprocess.check_output()函数?

126
我想在Python程序中运行以下命令: python py2.py -i test.txt 我尝试使用subprocess.check_output,如下所示:
py2output = subprocess.check_output(['python py2.py', '-i', 'test.txt'])
py2output = subprocess.check_output(['python', 'py2.py', '-i', 'test.txt'])

4
当它们无法工作时,具体发生了什么事情? - khagler
1
试图通过Python 3运行Python 2文件?不,这是不可能的。Python 3有意与Python 2不兼容。你必须首先运行2to3才能让你的文件在Python 3中工作。 - Makoto
2
@Makoto:他正在尝试在Python 3脚本内部运行Python 2解释器,这是完全合理的。(从他之前的问题中,我们知道他正是为了解决你正在思考的问题而进行这样的操作,但这并不重要。) - abarnert
2
另外,关于Python 2的整个事情,以及你对py2.py如何解释参数的期望,与问题无关。这让至少一个人(Makoto)感到困惑,他本来可能会给你一个好的答案,也可能会让其他人感到困惑或害怕。在将来,如果您能提供一个最小的示例,并消除所有不必要的干扰,您可能会得到更好的答案。 - abarnert
在上一个评论中点个赞。当我使用 subprocess 测试 Python 程序时,我倾向于使用 Unix 内置的 yes 命令,因为它非常轻量级,可以生成包含单个字符“y”的无限行数。 - Tom Swirly
显示剩余3条评论
4个回答

105

正确的答案是(使用Python 2.7及更高版本,因为check_output()在那时被引入):

py2output = subprocess.check_output(['python','py2.py','-i', 'test.txt'])

为了演示,这里有两个程序: py2.py:
import sys
print sys.argv

py3.py:

import subprocess
py2output = subprocess.check_output(['python', 'py2.py', '-i', 'test.txt'])
print('py2 said:', py2output)

运行它:

$ python3 py3.py
py2 said: b"['py2.py', '-i', 'test.txt']\n"

以下是每个版本存在的问题:

py2output = subprocess.check_output([str('python py2.py '),'-i', 'test.txt'])

首先,str('python py2.py')'python py2.py' 是完全相同的东西——你正在把一个str转换成另一个str。这会使代码更难以阅读、更冗长、甚至更慢,而且并没有增加任何好处。

更重要的是,python py2.py 不能作为单个参数,除非你真的想运行一个名为 /usr/bin/python\ py2.py 的程序。但你不是;你正在尝试运行如下命令:/usr/bin/python 并把 py2.py 作为第一个参数。因此,你需要在列表中将它们分开成两个元素。

你的第二个版本已经修复了这个问题,但你忘记了在 test.txt' 前面加上 ' 。这应该会导致一个SyntaxError,可能会说 EOL while scanning string literal

同时,我不确定你是如何找到文档但却找不到带有参数的示例的。第一个示例就是:

>>> subprocess.check_output(["echo", "Hello World!"])
b'Hello World!\n'

这调用了带有附加参数"Hello World!"的"echo"命令。

另外:

-i是argparse的位置参数,test.txt是-i所代表的内容。

我确信-i不是一个位置参数,而是一个可选参数。否则,句子的后半部分就没有意义了。


现在我尝试使用 argparse 中的 2 个参数。我试图将此命令放入 subprocess:python py2.py -i test.txt -l ong 我根据您的答案尝试了这个 subprocess:py2output=subprocess.check_output(["python","py2.py","-i","test.txt","-l","ong"]) 但它不起作用。 - JOHANNES_NYÅTT
2
@user1925847:你说的“它不起作用”是什么意思?这不是一个有帮助的评论。如果你做得对,它就会起作用——正如我的答案所证明的那样。所以显然你做错了什么。但是猜测你做错了什么是不可能的。正如我在主问题的评论中所说:向我们展示你的脚本,并告诉我们期望的和实际的输出是什么,否则就没有猜测你做错了什么的希望。 - abarnert
1
不要忘记捕获 CalledProcessError 和 OSError 异常。 - anatoly techtonik
1
我建议使用函数shlex.split("python py2.py -i test.txt")将字符串拆分为参数列表。 - compie
@AdrianKeister 为什么你要在一个完全不同的问题的评论中提到 subprocess?我99%确定你的问题已经作为自己的问题存在于SO上了。但简单来说:subprocess 运行程序。没有名为 cd 的程序。在 cmd 命令行解释器 shell(又称“DOS提示符”)中有一个内置命令叫做 cd,它可以更改该 shell 的当前目录。您可以运行一个新的 shell 来执行 cd(使用 ['cmd', '/c', 'cd', path] 或使用 shell=True),但这将是相当无意义的。 - abarnert
显示剩余2条评论

69
自从Python 3.5版本以来,建议使用subprocess.run而不是subprocess.check_output
>>> subprocess.run(['cat','/tmp/text.txt'], check=True, stdout=subprocess.PIPE).stdout
b'First line\nSecond line\n'

自从Python 3.7版本以后,你可以使用capture_output=True参数来捕获标准输出和标准错误输出,而不是上述的方法。
>>> subprocess.run(['cat','/tmp/text.txt'], check=True, capture_output=True).stdout
b'First line\nSecond line\n'

此外,您可能希望使用universal_newlines=True或其等效项,因为自Python 3.7以来,text=True可用于处理文本而非二进制数据。
>>> stdout = subprocess.run(['cat', '/tmp/text.txt'], check=True, capture_output=True, text=True).stdout
>>> print(stdout)
First line
Second line

1
在这个选项中,subprocess.run(['ls','-la'],stdout=subprocess.PIPE,universal_newlines=True)。stdout会为Python 3.6提供带有换行符的行。 - Higinio Fuentes
这个应该加上check=True才能和subprocess.check_output等价吧? - Mateen Ulhaq
@MateenUlhaq,你是正确的。已编辑。 - Gohu
@MateenUlhaq 所有的subprocess方法都有两种模式:传递一个字符串列表,使用shell=False(默认值);或者传递一个单独的字符串给shell解析,使用显式的shell=True。具体可行的方式也取决于你是在Windows还是类Unix平台上。另请参阅subprocess中shell=True的实际含义 - undefined

15

补充一下@abarnert提到的方法,更好的方法是捕获异常

import subprocess
try:
    py2output = subprocess.check_output(['python', 'py2.py', '-i', 'test.txt'],stderr= subprocess.STDOUT)  
    #print('py2 said:', py2output)
    print "here"
except subprocess.CalledProcessError as e:
    print "Calledprocerr"

这个 stderr= subprocess.STDOUT 是为了确保你不会在stderr中收到文件未找到错误,因为通常无法捕获filenotfoundexception异常,否则你最终会得到

python: can't open file 'py2.py': [Errno 2] No such file or directory

实际上,更好的解决方案可能是检查文件/脚本是否存在,然后运行文件/脚本。

1

@ravi.zombie的答案已经适配到Python 3,并且可以在出现非零退出代码错误时产生输出:

try:
    out = subprocess.check_output(['git','commit','-m', commit_message], stderr= subprocess.STDOUT ).decode()
except subprocess.CalledProcessError as e:
    print ('[ERROR]: Exit code != 0')
    out = e.output.decode()
print(out)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接