Python - 如何执行带有管道的shell命令,但不使用'shell=True'?

53

我有一个需求,想在Python中执行以下的Shell命令并获取其输出:

echo This_is_a_testing | grep -c test

我可以使用这段python代码在python中执行上述shell命令:

>>> import subprocess
>>> subprocess.check_output("echo This_is_a_testing | grep -c test", shell=True)
'1\n'

然而,由于我不想使用"shell=True"选项,因此我尝试了以下Python代码:

>>> import subprocess
>>> p1 = subprocess.Popen(["echo", "This_is_a_testing"], stdout=subprocess.PIPE)
>>> p2 = subprocess.Popen(["grep", "-c", "test"], stdin=p1.stdout)
>>> p1.stdout.close()
>>> p2.communicate()
(None, None)
我想知道为什么输出结果是“None”,因为我已经参考了网页上的描述:http://docs.python.org/library/subprocess.html#subprocess.PIPE 我的代码是否有所遗漏?有什么建议/想法吗?提前感谢。

如何使用subprocess.Popen通过管道连接多个进程? - jfs
5个回答

43
>>> import subprocess

>>> mycmd=subprocess.getoutput('df -h | grep home | gawk \'{ print $1 }\' | cut -d\'/\' -f3')

>>> mycmd 

'sda6'

>>>

2
它在内部使用shell。OP明确表示:“我不想使用“shell=True”选项”。 - jfs
18
谢谢。这正是我在搜索如何在Python中使用管道执行shell命令时所需要的内容。 - xpt
4
为了让这个工作起来,我不得不使用 commands.getoutput 而不是 subprocess.getoutput。 - Anake
1
难道不是 subprocess.check_output() 吗? - WesternGun
1
在Python 2.7中不存在,在Python 3.6.6中有。 - DavidJ
显示剩余3条评论

40

请看这里:

>>> import subprocess
>>> p1 = subprocess.Popen(["echo", "This_is_a_testing"], stdout=subprocess.PIPE)
>>> p2 = subprocess.Popen(["grep", "-c", "test"], stdin=p1.stdout)
>>> 1
p1.stdout.close()
>>> p2.communicate()
(None, None)
>>>

当你执行 p2 = subprocess.Popen(["grep", "-c", "test"], stdin=p1.stdout) 后,你会得到输出 1。在你的问题环境中不要忽略这个输出。

如果这正是你想要的结果,那么在第二个 Popen 的参数中传入 stdout=subprocess.PIPE:

>>> p1 = subprocess.Popen(["echo", "This_is_a_testing"], stdout=subprocess.PIPE)
>>> p2 = subprocess.Popen(["grep", "test"], stdin=p1.stdout, stdout=subprocess.PIPE)
>>> p2.communicate()
('This_is_a_testing\n', None)
>>>

现在我知道要让p2.communicate()正常工作,必须在代码p2 = ...中添加stdout=subprocess.PIPE。谢谢。 - user1129812
如果p1的结果出现错误,我该如何获取错误信息?这是我的问题,你能帮忙解决吗?http://stackoverflow.com/questions/40467105/how-to-catch-ping-error-with-python - prosto.vint

33

从手册中得知:

要获取除None以外的任何结果元组,您需要提供stdout=PIPE和/或stderr=PIPE

p2 = subprocess.Popen(["grep", "-c", "test"], stdin=p1.stdout, stdout=subprocess.PIPE)

6

虽然接受的答案是正确/可行的,但另一种选择是使用 Popen.communicate() 方法将某些内容传递到进程的标准输入中:

>>> import subprocess
>>> p2 = subprocess.Popen(["grep", "-c", "test"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
>>> p2.communicate("This_is_a_testing")
('1\n', None)
>>> print p2.returncode
0
>>>>

这解决了执行另一个命令来重定向输出的需求,如果输出已在Python脚本中知道。然而,`communicate`具有等待进程终止的副作用。如果需要/希望异步执行,则使用两个进程可能是更好的选择。

1
答案与之前提到的类似,只是格式略有不同。我想要得到与正常 shell 命令在 Python 3 中使用管道完全相同的输出结果。
import subprocess

p1 = subprocess.Popen(["ls", "-l", "."], stdout=subprocess.PIPE)
p2 = subprocess.Popen(["grep", "May"], stdin=p1.stdout, stdout=subprocess.PIPE)


for s in (str(p2.communicate())[2:-10]).split('\\n'):
    print(s)

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