我正在使用Pythonsubprocess.communicate()
从运行约一分钟的进程中读取stdout。
如何stdout
以流方式打印出该流程的每一行,以便可以看到生成的输出,但是仍然阻止该流程终止,然后再继续?
subprocess.communicate()
似乎一次给出所有输出。
Answers:
请注意,我认为JF Sebastian的方法(如下)更好。
这是一个简单的示例(不检查错误):
import subprocess
proc = subprocess.Popen('ls',
shell=True,
stdout=subprocess.PIPE,
)
while proc.poll() is None:
output = proc.stdout.readline()
print output,
如果ls
结束太快,则while循环可能在您读取所有数据之前结束。
您可以通过以下方式在stdout中捕获其余部分:
output = proc.communicate()[0]
print output,
while proc.poll() is None: time.sleep(0)
或实现某种效果有关。基本上,您需要确保输出换行符是该过程要做的最后一件事(因为您不能让解释器有时间再次循环),或者您需要做一些“花哨的事情”。
要在子进程刷新其标准输出缓冲区时逐行获取子进程的输出:
#!/usr/bin/env python2
from subprocess import Popen, PIPE
p = Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1)
with p.stdout:
for line in iter(p.stdout.readline, b''):
print line,
p.wait() # wait for the subprocess to exit
iter()
用于在编写行后立即读取行,以解决Python 2中的预读错误。
如果子进程的stdout在非交互模式下使用块缓冲而不是行缓冲(这会导致输出延迟,直到子缓冲区已满或被子进程显式刷新),然后您可以尝试使用以下方式强制使用无缓冲输出 pexpect
,pty
模块或unbuffer
,stdbuf
,script
公用事业,见问:为什么不直接使用管道(popen方法())?
这是Python 3代码:
#!/usr/bin/env python3
from subprocess import Popen, PIPE
with Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1,
universal_newlines=True) as p:
for line in p.stdout:
print(line, end='')
注意:与Python 2不同,Python 2照原样输出子进程的字节串。Python 3使用文本模式(使用locale.getpreferredencoding(False)
编码对cmd的输出进行解码)。
b''
是一个bytes
在Python 2.7和Python 3.字面
bufsize=1
如果您还向子进程写入(使用p.stdin
),可能会有所不同,例如,它可以帮助避免在进行交互式(类似pexpect
)交换时出现死锁-假设子进程本身没有缓冲问题。如果您只是在阅读,那么就像我说的那样,区别仅在于性能:如果不是,那么您能否提供一个最小的完整代码示例来说明这一点?
stderr=subprocess.STDOUT
Popen()
stdout=PIPE
未捕获输出(您仍然在屏幕上看到它),则您的程序可能会打印到stderr或直接打印到终端。要合并stdout&stderr,请通过stderr=subprocess.STDOUT
(请参阅我之前的评论)。要捕获直接打印到tty的输出,可以使用pexpect,pty解决方案。。这是一个更复杂的代码示例。
我相信以流方式从流程收集输出的最简单方法是这样的:
import sys
from subprocess import *
proc = Popen('ls', shell=True, stdout=PIPE)
while True:
data = proc.stdout.readline() # Alternatively proc.stdout.read(1024)
if len(data) == 0:
break
sys.stdout.write(data) # sys.stdout.buffer.write(data) on Python 3.x
该readline()
或read()
函数只应在EOF返回一个空字符串,进程终止后-否则,如果没有什么阅读(它会阻止readline()
包括换行,等空行,则返回“\ n”)。这样避免了communicate()
在循环后进行笨拙的最终调用。
对于行长很长的文件,read()
最好减少最大内存使用量-传递给它的数量是任意的,但如果排除该数量,则会导致立即读取整个管道输出,这可能是不希望的。
data = proc.stdout.read()
阻止,直到读取所有数据。您可能将它与os.read(fd, maxsize)
可以更早返回(一旦有任何可用数据)相混淆。
read()
它,那么它将正常工作,并且readline()
只要最大行长是合理的,同样可以正常工作。相应地更新了我的答案。
如果您只是想尝试实时传递输出,那么很难做到比这更简单:
import subprocess
# This will raise a CalledProcessError if the program return a nonzero code.
# You can use call() instead if you don't care about that case.
subprocess.check_call(['ls', '-l'])
请参阅有关subprocess.check_call()的文档。
如果需要处理输出,请确保对其进行循环。但是,如果不这样做,请保持简单。
编辑: JF Sebastian指出,stdout和stderr参数的默认值都传递给sys.stdout和sys.stderr,并且如果替换了sys.stdout和sys.stderr,这将失败(例如,用于捕获输出。测试)。
sys.stdout
或sys.stderr
与文件一样,并没有真正的fileno对象是()取代。如果sys.stdout
,sys.stderr
没有那么取代它就更简单了:subprocess.check_call(args)
。
call()
结束,check_call()
除非我想要CalledProcessError
。
python -mthis
:“错误绝不能静默传递。除非明确静默。” 这就是示例代码应优先check_call()
于的原因call()
。
call()
在非错误情况下都会返回非零错误代码,因为它们很糟糕。因此,在我们的案例中,非零错误代码实际上不是错误。
grep
即使没有错误也可能返回非零退出状态-它们是例外。默认情况下,零退出状态表示成功。
myCommand="ls -l"
cmd=myCommand.split()
# "universal newline support" This will cause to interpret \n, \r\n and \r equally, each as a newline.
p = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True)
while True:
print(p.stderr.readline().rstrip('\r\n'))
shlex.split(myCommand)
而不是myCommand.split()
。它也尊重引号中的空格。
添加另一个具有一些小的更改的python3解决方案:
with
构造时,我无法获取退出代码)import subprocess
import sys
def subcall_stream(cmd, fail_on_error=True):
# Run a shell command, streaming output to STDOUT in real time
# Expects a list style command, e.g. `["docker", "pull", "ubuntu"]`
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, universal_newlines=True)
for line in p.stdout:
sys.stdout.write(line)
p.wait()
exit_code = p.returncode
if exit_code != 0 and fail_on_error:
raise RuntimeError(f"Shell command failed with exit code {exit_code}. Command: `{cmd}`")
return(exit_code)