问题
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:
Ted told me walking in the command history with ESC [ A (up) and ESC [ B (down) works with bash.
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