捕获输出,包括子流程的控制字符



我有下面的简单程序来运行子流程,tee将其输出到stdout和一些缓冲区

import subprocess
import sys
import time
import unicodedata
p = subprocess.Popen(
"top",
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout_parts = []
while p.poll() is None:
for bytes in iter(p.stdout.readline, b''):
stdout_parts.append(bytes)
str = bytes.decode("utf-8")
sys.stdout.write(str)
for ch in str:
if unicodedata.category(ch)[0]=="C" and ord(ch) != 10:
raise Exception(f"control character! {ord(ch)}")
time.sleep(0.01)

当运行一些终端更新程序,如topdocker pull时,我希望能够捕获它的整个输出,即使它本身不是立即可读的。

阅读周围的命令如何在不附加到控制台的情况下更新top输出?例如,它似乎是通过控制字符来实现的。但是,在从进程输出流(stdout/stderr(读取行时,我没有收到任何行。还是他们使用的技术不同,而我无法从子流程中获取?

许多工具根据是否连接到终端来调整其输出。如果您希望在终端中以交互方式运行该工具时接收到的输出,请使用诸如pexpect之类的包装器来模拟这种行为。(还有一个低级别的pty库,但使用起来很棘手,尤其是如果你是问题领域的新手。(

某些工具还允许您为脚本指定批处理操作模式;也许可以查看CCD_ 7(尽管这在例如MacOS上不可用(。

为了记录,许多屏幕控制序列并不完全或甚至主要由控制字符组成;例如,将光标移动到curses中特定位置的控制序列以转义符(0x1B(开头,但在其他方面由常规可打印字符组成。如果您真的想处理这些序列,可以考虑使用curses/ANSI控制代码解析库。但对于大多数目的来说,更好的方法是使用机器可读的API并完全禁用屏幕更新。在Linux上,/proc伪文件系统提供了大量机器可读信息。

从还原编辑中回收的内容到问题:

用答案中的提示很好地打印top的一些解决方案:

import os
import pty
import subprocess
import sys
import time
import select
stdout_master_fd, stdout_slave_fd = pty.openpty()
stderr_master_fd, stderr_slave_fd = pty.openpty()
p = subprocess.Popen(
"top",
shell=True,
stdout=stdout_slave_fd,
stderr=stderr_slave_fd,
close_fds=True
)
stdout_parts = []
while p.poll() is None:
rlist, _, _ = select.select([stdout_master_fd, stderr_master_fd], [], [])
for f in rlist:
output = os.read(f, 1000)  # This is used because it doesn't block
sys.stdout.write(output.decode("utf-8"))
sys.stdout.flush()
time.sleep(0.01)

最新更新