从subprocess.Popen获取多个命令的输出

时间:2014-04-17 19:20:33

标签: python subprocess

我正在尝试运行命令,获取它的输出,然后在同一环境中运行另一个命令(比如我在第一个命令中设置了一个环境变量,我希望它可用于第二个命令命令)。我试过这个:

import subprocess

process = subprocess.Popen("/bin/bash", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE);

process.stdin.write("export MyVar=\"Test\"\n")
process.stdin.write("echo $MyVar\n")
process.stdin.flush()

stdout, stderr  = process.communicate()
print "stdout: " + str(stdout)

# Do it again
process.stdin.write("echo $MyVar\n")
process.stdin.flush()

stdout, stderr = process.communicate()
print "stdout: " + str(stdout)

但是communic()会一直读到最后,所以这不是一种有效的技术。 (我明白了:)

stdout: Test

Traceback (most recent call last):
  File "./MultipleCommands.py", line 15, in <module>
    process.stdin.write("echo $MyVar\n")
ValueError: I/O operation on closed file

我已经看到了这一点:https://stackoverflow.com/a/15654218/284529,但它没有提供一个如何做它所建议的工作示例。任何人都可以演示如何做到这一点? 我还看到了其他涉及在循环中不断检查输出的技术,但这并不适合&#34;得到命令的输出&#34;心态 - 它只是像流一样对待它。

4 个答案:

答案 0 :(得分:1)

communicate对象的{p> waitPopen方法,在进程返回后关闭PIPE。如果您希望与流程保持沟通,请尝试以下方法:

import subprocess

process = subprocess.Popen("/bin/bash", shell=True, stdin=subprocess.PIPE,       stdout=subprocess.PIPE, stderr=subprocess.PIPE);

process.stdin.write("export MyVar=\"Test\"\n")
process.stdin.write("echo $MyVar\n")
process.stdin.flush()

process.stdout.readline()

process.stdin.write("echo $MyVar\n")
process.stdin.flush()

stdout, stderr = process.communicate()
print "stdout: " + str(stdout)

我认为你误解了沟通......

看看这个链接: - http://docs.python.org/library/subprocess.html#subprocess.Popen.communicate

communication将一个字符串发送到另一个进程,然后等待它完成...(就像你说的等待EOF听stdout&amp; stderror)

你应该做的是:

proc.stdin.write('message')

# ...figure out how long or why you need to wait...

proc.stdin.write('message2')

(如果您需要获取stdout或stderr,请使用proc.stdout或proc.stderr)

答案 1 :(得分:1)

要获取多个命令的输出,只需将它们组合成一个脚本:

#!/usr/bin/env python
import subprocess
import sys

output = subprocess.check_output("""
export MyVar="Test"
echo $MyVar
echo ${MyVar/est/ick}
""", shell=True, executable='/bin/bash', universal_newlines=True)
sys.stdout.write(output)

输出

Test
Tick

答案 2 :(得分:0)

当使用communicate时,它看到子进程已经结束,但是如果你有一个中间的(bash),当你的子子进程结束时,你必须以某种方式手动发出信号。

至于其余部分,最简单的方法是发射标记线。但是,我很抱歉在这里让你失望但是汇集(即不断检查循环)实际上是the only sane option。如果您不喜欢循环,可以在函数中“隐藏”它。

import subprocess
import time

def readlines_upto(stream, until="### DONE ###"):
    while True:
        line = stream.readline()
        if line is None:
            time.sleep(0.1)
            continue
        if line.rstrip() == until:
            break
        yield line

process = subprocess.Popen("/bin/bash", shell=True,
    stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
process.stdin.write("export MyVar=\"Test\"\n")
process.stdin.write("echo $MyVar\n")
process.stdin.write("echo '### DONE ###'\n")
process.stdin.flush()

# Note, I don't read stderr here, so if subprocess outputs too much there,
# it'll fill the pipe and stuck. If you don't need stderr data, don't
# redirect it to a pipe at all. If you need it, make readlines read two pipes.
stdout = "".join(line for line in readlines_upto(process.stdout))
print "stdout: " + stdout

# Do it again
process.stdin.write("echo $MyVar\n")
process.stdin.flush()
stdout, stderr = process.communicate()
print "stdout: " + str(stdout)

答案 3 :(得分:0)

根据手册:

  

Popencommunicate输入=无

     

与流程交互:将数据发送到stdin。从stdout和stderr读取数据,直到达到文件结尾。 等待进程   终止。 [...]

您需要从管道中读取:

import os
stdout = os.read(process.stdout.fileno(), 1024)
print "stdout: " + stdout

如果没有数据等待,它将永久挂起或直到数据准备好被读取。您应该使用select系统调用来阻止:

import select
import os

try:
    i,o,e = select.select([process.stdout], [], [], 5) # 5 second timeout
    stdout = os.read(i[0].fileno(), 1024)
except IndexError:
    # nothing was written to the pipe in 5 seconds
    stdout = ""

print "stdout: " + stdout

如果你想获取多次写入,为了避免竞争条件,你必须把它放在一个循环中:

stdout = ""
while True:
    try:
        i,o,e = select.select([process.stdout], [], [], 5) # 5 second timeout
        stdout += os.read(i[0].fileno(), 1024)
    except IndexError:
        # nothing was written to the pipe in 5 seconds, we're done here
        break