我正在尝试获取全屏终端程序的输出,该程序使用重绘转义码来显示数据,并且需要tty
(或pty
)才能运行。
人类将遵循的基本程序是:
我想自动化这个过程。步骤4和5可以按任何顺序完成。虽然我的完美主义者担心屏幕状态的自我一致性,但我承认我并不确定如何正确定义这个(除了使用"它不仅仅是一定的自上次更新以来的超时时间")。
似乎使用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
输出以供进一步使用。具体做法是:
subprocess.PIPE
进行Popen
通话,因为目标程序无法正常工作。但是,我可以用一些更方便的方法将这些文件描述符包装成I / O吗?os.read
来电?我更习惯于read()
始终返回的类似文件的对象,如果到达流的末尾,则只返回一个空字符串。在这里,os.read
无论如何最终都会阻止。我还认为首先使用pty
和subprocess
并不是最好的方法。
答案 0 :(得分:1)
您可以使用pexpect
执行此操作。使用run()
函数获取数据,并查看附带的VT100 emulator(或pyte
)以进行渲染。
使用实用程序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))
(请注意,有时会出现导致extra line spacings的错误。)
答案 1 :(得分:1)
如果程序没有产生太多输出;最简单的方法是使用pexpect.run()
通过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)])
注意:
slave_fd
,而不是代码中使用master_fd
stdin