我正在尝试为命令行程序 (svnadmin verify) 编写一个包装器脚本,该脚本将显示操作的良好进度指示器。这要求我能够在输出时立即看到包装程序的每一行输出。
我认为我只需使用subprocess.Popen执行程序stdout=PIPE,然后读取每一行并相应地执行操作即可。但是,当我运行以下代码时,输出似乎被缓冲在某处,导致它出现在两个块中,即第 1 行到第 332 行,然后是第 333 行到第 439 行(输出的最后一行)
subprocess.Popen
stdout=PIPE
from subprocess import Popen, PIPE, STDOUT p = Popen('svnadmin verify /var/svn/repos/config', stdout = PIPE, stderr = STDOUT, shell = True) for line in p.stdout: print line.replace('\n', '')
在查看了有关子进程的文档之后,我发现了参数bufsize,Popen因此我尝试将 bufsize 设置为 1(缓冲每行)和 0(无缓冲),但这两个值似乎都没有改变行传递的方式。
bufsize
Popen
这时我开始抓住救命稻草,因此我写了以下输出循环:
while True: try: print p.stdout.next().replace('\n', '') except StopIteration: break
但结果是一样的。
是否可以使用子进程获取执行的程序的“实时”程序输出?Python 中还有其他向前兼容(不兼容exec*)的选项吗?
exec*
为了在Python中获取命令行程序(如svnadmin verify)的实时输出,可以尝试以下几种方法来确保标准输出没有被缓冲。
svnadmin verify
subprocess
select
使用 select 模块来实时读取子进程的输出:
import subprocess import select cmd = 'svnadmin verify /var/svn/repos/config' p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, bufsize=1, universal_newlines=True) while True: reads = [p.stdout.fileno()] ret = select.select(reads, [], []) if p.stdout.fileno() in ret[0]: output = p.stdout.readline() if output: print(output.strip()) else: break p.stdout.close() p.wait()
将输出手动刷新以确保实时输出:
import subprocess import sys cmd = 'svnadmin verify /var/svn/repos/config' p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, universal_newlines=True) while True: line = p.stdout.readline() if line: print(line.strip()) sys.stdout.flush() else: break p.stdout.close() p.wait()
有时候子进程会自己做缓冲,特别是当标准输出不是一个终端时。可以通过设置环境变量来禁用这种缓冲行为。
import os import subprocess cmd = 'svnadmin verify /var/svn/repos/config' env = os.environ.copy() env['PYTHONUNBUFFERED'] = '1' # 对于Python子进程 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, env=env, universal_newlines=True) while True: line = p.stdout.readline() if line: print(line.strip()) else: break p.stdout.close() p.wait()
通过上述方法,可以实时获取子进程的输出。select 模块方法更加通用,可以处理不同类型的子进程输出。手动刷新输出缓冲区可以确保及时显示。设置环境变量可以防止子进程自己做缓冲。根据需要选择合适的方法,以便实现实时输出和进度指示。