Python 如何从使用屏幕重画的程序中获得输出,以便在终端屏幕刮板中使用?
我正在尝试获取全屏终端程序的输出,该程序使用重画转义码来显示数据,并且需要运行Python 如何从使用屏幕重画的程序中获得输出,以便在终端屏幕刮板中使用?,python,subprocess,file-descriptor,pty,Python,Subprocess,File Descriptor,Pty,我正在尝试获取全屏终端程序的输出,该程序使用重画转义码来显示数据,并且需要运行tty(或pty) 人类将遵循的基本程序是: 在终端中启动程序 该程序使用重画来显示和更新各种数据字段 人类等待直到显示一致(可能使用诸如“它没有闪烁”或“自上次更新以来已经0.5秒了”之类的提示) 人类观察特定位置的字段并记住或记录数据 人类退出程序 然后,人员根据该数据在程序外执行操作 我想自动化这个过程。步骤4和5可以按任意顺序进行。虽然我身上的完美主义者担心屏幕状态的自我一致性,但我承认我并不确定如何正确定义这
tty
(或pty
)
人类将遵循的基本程序是:
pty
和subprocess
然后使用某种屏幕刮刀似乎是一种可行的方法,但我不清楚如何将它们一起使用,以及我使用的一些较低级别对象存在哪些危险
考虑一下这个计划:
#!/usr/bin/env python2
import os
import pty
import subprocess
import time
import pexpect.ANSI
# Psuedo-terminal FDs
fd_master, fd_slave = pty.openpty()
# Start 'the_program'
the_proc = subprocess.Popen(['the_program'], stdin=fd_master, stdout=fd_slave, stderr=fd_slave)
# Just kill it after a couple of seconds
time.sleep(2)
the_proc.terminate()
# Read output into a buffer
output_buffer = b''
read_size = None
while (read_size is None) or (read_size > 0):
chunk = os.read(fd_master, 1024)
output_buffer += chunk
read_size = len(chunk)
print("output buffer size: {:d}".format(len(output_buffer)))
# Feed output to screen scraper
ansi_term = pexpect.ANSI.ANSI(24, 80)
ansi_term.write(output_buffer)
# Parse presented data...
一个问题是,os.read()
调用总是阻塞。我还想知道是否有更好的方法获得pty
输出以供进一步使用。具体而言:
Popen
调用使用subprocess.PIPE
,因为这样目标程序就不能工作了。但是,我可以用一些更方便的方法来完成I/O吗os.read
调用上阻塞?我更习惯于类似文件的对象,其中read()
总是返回,如果到达流的末尾,只返回一个空字符串。在这里,os.read
不管发生什么最终都会阻塞pty
和子流程
并不是最好的方法。您可以使用它。使用run()
以实用工具top
为例:
import time
import pexpect
import pexpect.ANSI
# Start 'top' and quit after a couple of seconds
output_buffer = pexpect.run('top', timeout=2)
# For continuous reading/interaction, you would need to use the "events"
# arg, threading, or a framework for asynchronous communication.
ansi_term = pexpect.ANSI.ANSI(24, 80)
ansi_term.write(output_buffer)
print(str(ansi_term))
(请注意,有时会出现错误。)如果程序没有生成太多输出;最简单的方法是通过pty
获取其输出:
import pexpect # $ pip install pexpect
output, status = pexpect.run('top', timeout=2, withexitstatus=1)
您可以通过将输出与以前的输出进行比较来检测输出是否“稳定”:
import pexpect # $ pip install pexpect
def every_second(d, last=[None]):
current = d['child'].before
if last[0] == current: # "settled down"
raise pexpect.TIMEOUT(None) # exit run
last[0] = current
output, status = pexpect.run('top', timeout=1, withexitstatus=1,
events={pexpect.TIMEOUT: every_second})
您可以使用与输出中的循环模式匹配的正则表达式,而不是超时。目的是确定输出何时“稳定”
下面是直接使用subprocess
和pty
模块的代码比较:
#!/usr/bin/env python
"""Start process; wait 2 seconds; kill the process; print all process output."""
import errno
import os
import pty
import select
from subprocess import Popen, STDOUT
try:
from time import monotonic as timer
except ImportError:
from time import time as timer
output = []
master_fd, slave_fd = pty.openpty() #XXX add cleanup on exception
p = Popen(["top"], stdin=slave_fd, stdout=slave_fd, stderr=STDOUT,
close_fds=True)
os.close(slave_fd)
endtime = timer() + 2 # stop in 2 seconds
while True:
delay = endtime - timer()
if delay <= 0: # timeout
break
if select.select([master_fd], [], [], delay)[0]:
try:
data = os.read(master_fd, 1024)
except OSError as e: #NOTE: no need for IOError here
if e.errno != errno.EIO:
raise
break # EIO means EOF on some systems
else:
if not data: # EOF
break
output.append(data)
os.close(master_fd)
p.terminate()
returncode = p.wait()
print([returncode, b''.join(output)])
#/usr/bin/env python
“”“启动进程;等待2秒;终止进程;打印所有进程输出。”“”
输入错误号
导入操作系统
进口公司
导入选择
从子流程导入Popen、STDOUT
尝试:
从时间导入单调的计时器
除恐怖外:
从时间导入时间作为计时器
输出=[]
master_fd,slave_fd=pty.openpty()#XXX添加异常清理
p=Popen([“top”],stdin=slave\u fd,stdout=slave\u fd,stderr=stdout,
关闭(fds=真)
操作系统关闭(从系统)
endtime=timer()+2#2秒后停止
尽管如此:
延迟=结束时间-计时器()
如果延迟“做这个”——这是什么?如果您忘记了pty
,子流程
,那么您试图解决的问题是什么?顺便说一句,top
不是一个简单的例子。一个简单的例子是,如果一个程序被重定向,它只会稍微改变它的行为,例如,它改变它的缓冲模式,或者它停止使用ANSI转义(例如,对于颜色)。或者,如果您需要pty
在正常stdin/stdout之外传递密码,我可以理解。控制top
——一个全屏程序可能是另一个问题。@J.F.Sebastian-我想做的是获取一个使用重画转义序列的程序的输出,这样我就可以观察它显示的数据(可能通过将它输入ANSI屏幕抓取实用程序或其他方式)top
是我能找到的最简单的一个程序例子,它可以做到这一点,无论它如何运行(或者更具体地说,除非它认为它在tty
下,否则不会运行)。可能还有其他人,但如果他们在重定向时更改了操作,他们就不会说明这一概念。@J.F.Sebastian我应该指出,我最初只是问如何控制一个使用重画代码显示的程序,但它是O/t。这个问题试图使这一点更加具体。因此,我不愿意把它说得更笼统一些。我之所以这样问,是因为我觉得你的问题很像。假设您已经编写了脚本:在不查看其源代码的情况下,您将如何定义其观察到的行为?@J.F.Sebastian我同意,但询问非XY版本的问题基本上减少为图书馆推荐请求,这是O/T。唯一可观察到的行为是,给定显示特定数据的内部程序的输出,它将在该输出中生成特定字段的值。(编辑后)如何从程序中获取输出,以传递给VT100
仿真器的write()
方法?不幸的是,作者承认他没有一个例子。因此,从这个例子开始:然后添加到它,分配虚拟ANSI终端并写入传入的dat