在python中运行shell命令并读取输出

在python中运行shell命令并读取输出,python,unix,Python,Unix,我有以下资料: cmd = "ps aux | grep 'java -jar' | grep -v grep | awk '//{print $2}'".split(' ') p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() print out 当我在控制台(python之外)中运行该命令时,我得到了所需的输出。在python中运行上述代码将

我有以下资料:

cmd = "ps aux | grep 'java -jar' | grep -v grep | awk '//{print $2}'".split(' ')
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
print out
当我在控制台(python之外)中运行该命令时,我得到了所需的输出。在python中运行上述代码将打印一个空行。我假设
cmd
(特别是
|
操作符)有问题,但我不能确定


我需要通过标准的Python 2.6.6安装(无其他模块)来实现这一点。

您需要对原始命令的每一部分使用单个调用
Popen()
,如中所示

import subprocess

p1 = subprocess.Popen(["ps", "aux"], stdout=subprocess.PIPE,  stderr=subprocess.PIPE)
p2 = subprocess.Popen(["grep", "java -jar"], stdin=p1.stdout, stdout=subprocess.PIPE,  stderr=subprocess.PIPE)
p3 = subprocess.Popen(["grep", "-v", "grep"], stdin=p2.stdout, stdout=subprocess.PIPE,  stderr=subprocess.PIPE)
p4 = subprocess.Popen(["awk", "//{print $2}"], stdin=p3.stdout, stdout=subprocess.PIPE,  stderr=subprocess.PIPE)
out, err = p4.communicate()

print out

有一个深入的讨论。

shell管道中只有一个命令,Python代码很难替换它。因此,您可以启动几个外部进程并连接它们的输入和输出,但我只需要启动
ps aux
,并添加一些Python代码来过滤和提取所需的数据:

from subprocess import PIPE, Popen


def main():
    process = Popen(['ps', 'aux'], stdout=PIPE)
    pids = [
        line.split(None, 2)[1] for line in process.stdout if 'java -jar' in line
    ]
    process.wait()
    print '\n'.join(pids)


if __name__ == '__main__':
    main()

Popen
默认情况下只执行可执行文件,不执行shell命令行。 当您将参数列表传递给
Popen
时,它们应该调用one可执行文件及其参数:

import subprocess

proc = subprocess.Popen(['ps', 'aux'])
还请注意,您不应使用
str.split
分割命令,因为:

>>> "ps aux | grep 'java -jar'     | grep -v grep | awk '//{print $2}'".split(' ')
['ps', 'aux', '|', 'grep', "'java", "-jar'", '', '', '', '', '|', 'grep', '-v', 'grep', '|', 'awk', "'//{print", "$2}'"]
请注意:

  • 引用的参数(例如,
    'java-jar')
    被拆分
  • 如果有多个连续空格,则会得到一些空参数
Python已经提供了一个模块,知道如何以合理的方式拆分命令行,它是:

注意引用的参数是如何被保留的,多个空格是如何被优雅地处理的。您仍然无法将结果传递给
Popen
,因为
Popen
默认情况下将
|
解释为管道

如果要运行shell命令行(即使用任何shell功能,如管道、路径扩展、重定向等),必须传递
shell=True
。在这种情况下,您应该将字符串列表作为参数传递给
Popen
,而只传递一个作为完整命令行的字符串:

proc = subprocess.Popen("ps aux | grep 'java -jar' | grep -v grep | awk '//{print $2}'", shell=True)
如果使用
shell=True
传递字符串列表,其含义不同:第一个元素应该是完整的命令行,而其他元素作为选项传递给所使用的shell。例如,在我的机器上,默认shell(
sh
)有一个
-x
选项,该选项将显示在
stderr
上执行的所有进程:

>>> from subprocess import Popen
>>> proc = Popen(['ps aux | grep python3', '-x'], shell=True)
>>> 
username   7301  0.1  0.1  39440  7408 pts/9    S+   12:57   0:00 python3
username   7302  0.0  0.0   4444   640 pts/9    S+   12:58   0:00 /bin/sh -c ps aux | grep python3 -x
username   7304  0.0  0.0  15968   904 pts/9    S+   12:58   0:00 grep python3
在这里,您可以看到启动了一个执行命令的
/bin/sh
,该命令带有
-x
选项

(这些都记录在的文档中)


也就是说,实现目标的一种方法是:


但是,这在python中不可用如果您的操作系统提供了pgrep命令,则可能的副本将不可用,使用该命令比以这种方式过滤ps的输出要好得多。可能的副本显然不是副本-具体问题是管道操作符。
>>> from subprocess import Popen
>>> proc = Popen(['ps aux | grep python3', '-x'], shell=True)
>>> 
username   7301  0.1  0.1  39440  7408 pts/9    S+   12:57   0:00 python3
username   7302  0.0  0.0   4444   640 pts/9    S+   12:58   0:00 /bin/sh -c ps aux | grep python3 -x
username   7304  0.0  0.0  15968   904 pts/9    S+   12:58   0:00 grep python3
subprocess.check_output("ps aux | grep 'java -jar' | grep -v grep | awk '//{print $2}'", shell=True)
proc = subprocess.Popen("ps aux | grep 'java -jar' | grep -v grep | awk '//{print $2}'", shell=True, stdout=subprocess.PIPE)
out, err = proc.communicate()
from subprocess import Popen, PIPE

ps = Popen(['ps', 'aux'], stdout=PIPE)
grep_java = Popen(['grep', 'java -jar'], stdin=ps.stdout, stdout=PIPE)
grep_grep = Popen(['grep', '-v', 'grep'], stdin=grep_java.stdout, stdout=PIPE)
awk = Popen(['awk', '//{print $2}'], stdin=grep_grep.stdout, stdout=PIPE)
out, err = awk.communicate()

grep_grep.wait()
grep_java.wait()
ps.wait()