Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 用Popen控制less_Python_Python 3.x_Unix_Subprocess_Less Unix - Fatal编程技术网

Python 用Popen控制less

Python 用Popen控制less,python,python-3.x,unix,subprocess,less-unix,Python,Python 3.x,Unix,Subprocess,Less Unix,我试图通过MacOSX上的Python脚本控制更少的。基本上,我希望能够转发控制字符(上/下/左/右),但在Python程序中处理其他输入。我习惯于从开始减少,但是。因此,我不确定如何将任何字符发送到less 程序打开的时间更短,等待一秒钟,然后尝试使用两个单独的通道发送q退出:stdin和/dev/tty(因为我在上面链接的SO问题中提到了它)。两者都不起作用 from subprocess import Popen, PIPE import time p1 = Popen("echo hel

我试图通过MacOSX上的Python脚本控制
更少的
。基本上,我希望能够转发控制字符(上/下/左/右),但在Python程序中处理其他输入。我习惯于从
开始减少
,但是。因此,我不确定如何将任何字符发送到less

程序打开的时间更短,等待一秒钟,然后尝试使用两个单独的通道发送
q
退出:
stdin
/dev/tty
(因为我在上面链接的SO问题中提到了它)。两者都不起作用

from subprocess import Popen, PIPE
import time
p1 = Popen("echo hello | less -K -R", stdin=PIPE, shell=True)
time.sleep(1)
p1.stdin.write(bytes('q', 'utf-8'))
with open("/dev/tty", 'w') as tty:
    tty.write('q')
p1.wait()

如何从Python脚本控制
less

这有点复杂,但可以使用创建一个新的TTY,在其中您可以完全控制
less
,将输入和输出转发到原始TTY,使其感觉无缝

下面的代码使用Python 3及其标准库。可以做很多繁重的工作,但Python不支持。而且这种方式更有教育意义

import contextlib
import fcntl
import io
import os
import pty
import select
import signal
import struct
import termios
import time
import tty
假设代码的其余部分缩进以在此上下文管理器中运行

with contextlib.ExitStack() as stack:
我们需要获取真实的TTY并将其设置为原始模式。这可能会混淆TTY的其他用户(例如,程序退出后的shell),因此请确保在退出后将其恢复到相同的状态

tty_fd = os.open('/dev/tty', os.O_RDWR | os.O_CLOEXEC)
stack.callback(os.close, tty_fd)
tc = termios.tcgetattr(tty_fd)
stack.callback(termios.tcsetattr, tty_fd, termios.TCSANOW, tc)
tty.setraw(tty_fd, when=termios.TCSANOW)
然后我们可以调用
forkpty
,它在Python中被命名为
pty.fork()
。这有两件事:

  • 创建一个
  • 叉出一个新的孩子
  • 将子设备连接到PTY的从属端
  • 将子PID和PTY的主端返回到原始流程
孩子应该运行
less
。请注意使用,因为在
fork
之后继续执行其他代码可能不安全

child_pid, master_fd = pty.fork()
if child_pid == 0:
    os.execv('/bin/sh', ('/bin/sh', '-c', 'echo hello | less -K -R'))
    os._exit(0)
stack.callback(os.close, master_fd)
然后需要做一些工作来设置一些异步信号处理程序

  • 当子进程更改状态(例如退出)时,将收到
    SIGCHLD
    。我们可以用它来跟踪孩子是否还在跑步
  • 当控制终端改变大小时,接收到
    SIGWINCH
    。我们将此大小转发给PTY(PTY将自动向附加到它的进程发送另一个窗口更改信号)。我们也应该将PTY的窗口大小设置为与开始匹配
转发诸如
SIGINT
SIGTERM
等信号也有意义

child_is_running = True
def handle_chld(signum, frame):
    while True:
        pid, status = os.waitpid(-1, os.P_NOWAIT)
        if not pid:
            break
        if pid == child_pid:
            child_is_running = False
def handle_winch(signum, frame):
    tc = struct.pack('HHHH', 0, 0, 0, 0)
    tc = fcntl.ioctl(tty_fd, termios.TIOCGWINSZ, tc)
    fcntl.ioctl(master_fd, termios.TIOCSWINSZ, tc)
handler = signal.signal(signal.SIGCHLD, handle_chld)
stack.callback(signal.signal, signal.SIGCHLD, handler)
handler = signal.signal(signal.SIGWINCH, handle_winch)
stack.callback(signal.signal, signal.SIGWINCH, handler)
handle_winch(0, None)
现在来看看真正的肉:在真假TTY之间复制数据

target_time = time.clock_gettime(time.CLOCK_MONOTONIC_RAW) + 1
has_sent_q = False
with contextlib.suppress(OSError):
    while child_is_running:
        now = time.clock_gettime(time.CLOCK_MONOTONIC_RAW)
        if now < target_time:
            timeout = target_time - now
        else:
            timeout = None
            if not has_sent_q:
                os.write(master_fd, b'q')
                has_sent_q = True
        rfds, wfds, xfds = select.select((tty_fd, master_fd), (), (), timeout)
        if tty_fd in rfds:
            data = os.read(tty_fd, io.DEFAULT_BUFFER_SIZE)
            os.write(master_fd, data)
        if master_fd in rfds:
            data = os.read(master_fd, io.DEFAULT_BUFFER_SIZE)
            os.write(tty_fd, data)
target\u time=time.clock\u gettime(time.clock\u单调\u RAW)+1
_发送了吗_q=False
使用contextlib.suppress(操作错误):
当child_运行时:
现在=time.clock\u gettime(time.clock\u单调的\u原始)
如果现在<目标时间:
超时=目标时间-现在
其他:
超时=无
如果没有发送,请发送:
操作系统写入(master_fd,b'q')
_发送了吗_q=True
rfds,wfds,xfds=select.select((tty\u fd,master\u fd),(),(),超时)
如果投标文件中有tty_fd:
数据=os.read(tty\U fd,io.DEFAULT\U BUFFER\U大小)
操作系统写入(主控单元fd,数据)
如果RFD中的主功能失效:
data=os.read(主\u fd,io.DEFAULT\u缓冲区\u大小)
操作系统写入(tty\U fd,数据)

它看起来很简单,虽然我在掩饰一些事情,比如适当的短写和
SIGTTIN
/
sigttoo
处理(通过抑制
OSError
部分隐藏)。

这闻起来像是一个-我能问一下为什么要这样做吗?@match我想用它在命令行上显示字典条目,如果工作流应为键入word,请使用
less
->输入->显示条目键入word,然后输入->显示
less
。。。我不想要求每次查找都少用“q”来退出。PythonPager太有限了,使用less的键文件功能在除箭头以外的任何键上退出都需要用户在键入下一个单词之前按一些键退出。如果有一种方法可以在
less
中按下最后一个键,那么它也可以解决我的问题。所以您只想显示前n行一秒钟?
head
能不能代替
less
?@l3via比No更有效,一秒钟的暂停和退出只是为了演示;我不希望
less
立即退出,因为这样很难判断它是否运行。实际上,我需要控制
less
程序,或者另一个合适的寻呼机。在原始模式下使用tty的程序不能很好地使用管道。您需要使用pty与之交互,并将父设备作为主/控制终端。感谢您的编写!“直截了当”,呵呵。我会试试看,看看我能做些什么。它还包含跨平台支持,这是一个优点。@NateGlenn-Heh是的,这有点深奥。“直飞”部分使用的是
select
循环,但使用
pty
/
termios
相对较少。
pexpect.spawn().interactive()
的一些组合应该可以让您实现大部分功能,您只需连接
WINCH
和一些其他东西,您可以从这里或pexpect源代码复制。祝你好运