How do I interact with a child process pretending to be a terminal?

安稳与你 提交于 2019-12-10 13:47:38

问题


I'm trying to interact with an NCURSES program.

As an example I'm using GNU Screen and run aptitude inside. (you could try it with mc instead.)

The program below starts a screen session with -x to connect to my session.

I want to navigate by pressing Arrow-down and Arrow-up.

If I send 'q' for quit I see a box pop up in my other screen session.

What do I need to do to get special keys like arrow keys working?

It currently seems to ignore the VT102 sequence I'm sending.

from twisted.internet import protocol, reactor

class MyPP(protocol.ProcessProtocol):
    def connectionMade(self):
        reactor.callLater(1.0, self.foo)

    def foo(self):
        self.transport.write('\033[B')

    def processExited(self, reason):
        print "processExited, status %s" % (reason.value.exitCode,)

    def outReceived(self, data):
        print data

    def errReceived(self, data):
        print "errReceived!", data

pp = MyPP()
command = ['screen', '-x']
reactor.spawnProcess(pp, command[0], command, {'TERM':'xterm'}, usePTY=True)

reactor.run()

UPDATE:

  1. Ted told me walking in the command history with ESC [ A (up) and ESC [ B (down) works with bash.

  2. Wondering why in aptitude it doesn't I've changed TERM=xterm to TERM=ansi which fixes it. Why xterm doesn't work still puzzles me.


回答1:


I've changed TERM=xterm to TERM=ansi which fixes it. Why xterm doesn't work still puzzles me.

Using Ubuntu 13.04, it looks like the ansi and xterm control codes aren't quite the same.

$ infocmp ansi | grep cud
        cr=^M, cub=\E[%p1%dD, cub1=\E[D, cud=\E[%p1%dB, cud1=\E[B,
        kcud1=\E[B, kcuf1=\E[C, kcuu1=\E[A, khome=\E[H, kich1=\E[L,

$ infocmp xterm | grep cud
        cud=\E[%p1%dB, cud1=^J, cuf=\E[%p1%dC, cuf1=\E[C,
        kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA,

...so it looks like you need to send the string '\033OB' to emulate a down arrow with xterm.

The following code works for me...

import subprocess
import os
import time

# Set TERM=xterm in case it isn't already
os.environ['TERM'] = 'xterm'

# Spawn aptitude
p = subprocess.Popen('aptitude', stdin=subprocess.PIPE)

# Wait for a bit to let it load from cache
time.sleep(5)

# Control it using xterm control codes
p.stdin.write('\033OB') # arrow down
time.sleep(1)
p.stdin.write('\033OB') # arrow down
time.sleep(1)
p.stdin.write('\033OA') # arrow up
time.sleep(1)
p.stdin.write('\033OA') # arrow up
time.sleep(1)
p.stdin.write('q')      # quit
time.sleep(1)
p.stdin.write('y')      # confirm

...although it screwed up my terminal after completion, so I had to do...

$ stty sane

...to get it working again.


Update

Just found what might be an easier way to determine the correct control codes. If you load vi, go into insert mode, then press CTRL-V followed by the key you want to emulate, it shows the literal string sent from the terminal.

For example...

Down Arrow: ^[OB

Page Up: ^[[5~

...where ^[ is CTRL-[, i.e. '\033'.




回答2:


A good method to obtain codes for particular terminal functions is using the tput command, for some particular terminal type with -T option.

In Python, use the curses module to obtain correct codes:

from curses import *
setupterm('xterm')

key_up = tigetstr("kcuul")
key_down = tigetstr("kcudl")

You can read about available capabilities by launching man terminfo. The example above may need savetty() before setupterm and resetty() after you obtain the key codes you are interested in. Otherwise, your terminal may be left in a bad state. In C it was good to have that in some exit handler as well, to reset terminal on error, but the Python module may handle that in its own.

This method, in contrast to hardcoding the terminal codes, has the advantage of being portable between systems, where terminfo for xterm may be different than that on current Linux distributions.




回答3:


Perhaps something like Pexpect might by useful here:

https://pypi.python.org/pypi/pexpect

Its a python implementation of Expect, which basically watches input and based on patterns performs actions as if a person was sitting there interacting with the app.



来源:https://stackoverflow.com/questions/17005775/how-do-i-interact-with-a-child-process-pretending-to-be-a-terminal

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!