Chr*_*ieb 124 python subprocess
我正在尝试为命令行程序(svnadmin verify)编写一个包装器脚本,它将为操作显示一个很好的进度指示器.这要求我能够在输出后立即查看包装程序的每一行输出.
我想我只是执行程序使用subprocess.Popen,使用stdout=PIPE,然后读取每一行,并相应地对其进行操作.但是,当我运行以下代码时,输出似乎在某处缓冲,导致它出现在两个块中,第1行到第332行,然后是333到439(输出的最后一行)
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', '')
Run Code Online (Sandbox Code Playgroud)
稍微查看子进程的文档后,我发现bufsize参数为Popen,所以我尝试将bufsize设置为1(缓冲每行)和0(无缓冲区),但这两个值似乎都没有改变行的传递方式.
此时我开始掌握吸管,所以我编写了以下输出循环:
while True:
try:
print p.stdout.next().replace('\n', '')
except StopIteration:
break
Run Code Online (Sandbox Code Playgroud)
但得到了相同的结果.
是否有可能获得使用子进程执行的程序的"实时"程序输出?Python中是否有其他选项可以向前兼容(不是exec*)?
Dav*_*ave 78
我尝试了这个,并且由于某种原因而在代码中
for line in p.stdout:
...
Run Code Online (Sandbox Code Playgroud)
缓慢地缓冲,变种
while True:
line = p.stdout.readline()
if not line: break
...
Run Code Online (Sandbox Code Playgroud)
才不是.显然这是一个已知的错误:http://bugs.python.org/issue3907(该问题现已截至2018年8月29日"已关闭")
Cor*_*erg 37
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=1)
for line in iter(p.stdout.readline, b''):
print line,
p.stdout.close()
p.wait()
Run Code Online (Sandbox Code Playgroud)
Nad*_*mli 18
你可以试试这个:
import subprocess
import sys
process = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
while True:
out = process.stdout.read(1)
if out == '' and process.poll() != None:
break
if out != '':
sys.stdout.write(out)
sys.stdout.flush()
Run Code Online (Sandbox Code Playgroud)
如果使用readline而不是read,则会出现一些未打印输入消息的情况.尝试使用命令需要内联输入并亲自查看.
Aid*_*man 18
您可以直接将子进程输出定向到流.简化示例:
subprocess.run(['ls'], stderr=sys.stderr, stdout=sys.stdout)
Run Code Online (Sandbox Code Playgroud)
在 Python 3.x 中,该过程可能会挂起,因为输出是字节数组而不是字符串。确保将其解码为字符串。
从 Python 3.6 开始,您可以使用Popen Constructor 中的参数encoding来完成。完整示例:
process = subprocess.Popen(
'my_command',
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True,
encoding='utf-8',
errors='replace'
)
while True:
realtime_output = process.stdout.readline()
if realtime_output == '' and process.poll() is not None:
break
if realtime_output:
print(realtime_output.strip(), flush=True)
Run Code Online (Sandbox Code Playgroud)
请注意,此代码重定向 stderr到stdout并处理输出错误。
小智 6
实时输出问题已解决:我在 Python 中遇到了类似的问题,同时捕获 C 程序的实时输出。我添加fflush(stdout);了我的 C 代码。它对我有用。这是代码。
C程序:
#include <stdio.h>
void main()
{
int count = 1;
while (1)
{
printf(" Count %d\n", count++);
fflush(stdout);
sleep(1);
}
}
Run Code Online (Sandbox Code Playgroud)
蟒蛇程序:
#!/usr/bin/python
import os, sys
import subprocess
procExe = subprocess.Popen(".//count", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
while procExe.poll() is None:
line = procExe.stdout.readline()
print("Print:" + line)
Run Code Online (Sandbox Code Playgroud)
输出:
Print: Count 1
Print: Count 2
Print: Count 3
Run Code Online (Sandbox Code Playgroud)
根据用例,您可能还想禁用子进程本身的缓冲。
如果子进程是一个 Python 进程,您可以在调用之前执行以下操作:
os.environ["PYTHONUNBUFFERED"] = "1"
Run Code Online (Sandbox Code Playgroud)
或者将其在env参数中传递给Popen.
否则,如果您使用的是 Linux/Unix,则可以使用该stdbuf工具。例如:
cmd = ["stdbuf", "-oL"] + cmd
Run Code Online (Sandbox Code Playgroud)
另请参阅此处有关stdbuf或其他选项。
(另请参阅此处以获得相同的答案。)
该流子stdin和stdout与ASYNCIO在Python的博客文章凯文·麦卡锡显示了如何ASYNCIO做到这一点:
import asyncio
from asyncio.subprocess import PIPE
from asyncio import create_subprocess_exec
async def _read_stream(stream, callback):
while True:
line = await stream.readline()
if line:
callback(line)
else:
break
async def run(command):
process = await create_subprocess_exec(
*command, stdout=PIPE, stderr=PIPE
)
await asyncio.wait(
[
_read_stream(
process.stdout,
lambda x: print(
"STDOUT: {}".format(x.decode("UTF8"))
),
),
_read_stream(
process.stderr,
lambda x: print(
"STDERR: {}".format(x.decode("UTF8"))
),
),
]
)
await process.wait()
async def main():
await run("docker build -t my-docker-image:latest .")
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
92046 次 |
| 最近记录: |