How do I press enter with pexpect [duplicate] - python

I am working with pythons pexpect module to automate tasks, I need help in figuring out key characters to use with sendcontrol. how could one send the controlkey ENTER ? and for future reference how can we find the key characters?
here is the code i am working on.
#!/usr/bin/env python
import pexpect
id = pexpect.spawn ('ftp 192.168.3.140')
id.expect_exact('Name')
id.sendline ('anonymous')
id.expect_exact ('Password')
*# Not sure how to send the enter control key
id.sendcontrol ('???')*
id.expect_exact ('ftp')
id.sendline ('dir')
id.expect_exact ('ftp')
lines = id.before.split ('\n')
for line in lines :
print line

pexpect has no sendcontrol() method. In your example you appear to be trying to send an empty line. To do that, use:
id.sendline('')
If you need to send real control characters then you can send() a string that contains the appropriate character value. For instance, to send a control-C you would:
id.send('\003')
or:
id.send(chr(3))
Responses to comment #2:
Sorry, I typo'ed the module name -- now fixed. More importantly, I was looking at old documentation on noah.org instead of the latest documentation at SourceForge. The newer documentation does show a sendcontrol() method. It takes an argument that is either a letter (for instance, sendcontrol('c') sends a control-C) or one of a variety of punctuation characters representing the control characters that don't correspond to letters. But really sendcontrol() is just a convenient wrapper around the send() method, which is what sendcontrol() calls after after it has calculated the actual value that you want to send. You can read the source for yourself at line 973 of this file.
I don't understand why id.sendline('') does not work, especially given that it apparently works for sending the user name to the spawned ftp program. If you want to try using sendcontrol() instead then that would be either:
id.sendcontrol('j')
to send a Linefeed character (which is control-j, or decimal 10) or:
id.sendcontrol('m')
to send a Carriage Return (which is control-m, or decimal 13).
If those don't work then please explain exactly what does happen, and how that differs from what you wanted or expected to happen.

If you're just looking to "press enter", you can send a newline:
id.send("\n")
As for other characters that you might want to use sendcontrol() with, I found this useful: https://condor.depaul.edu/sjost/lsp121/documents/ascii-npr.htm
For instance, I was interested in Ctrl+v. Looking it up in the table shows this line:
control character
python & java
decimal
description
^v
\x16
22
synchronous idle
So if I want to send that character, I can do any of these:
id.send('\x16')
id.send(chr(22))
id.sendcontrol('v')
sendcontrol() just looks up the correct character to send and then sends it like any other text
For keys not listed in that table, you can run this script: https://github.com/pexpect/pexpect/blob/master/tests/getch.py (ctrl space to exit)
For instance, ran that script and pressed F4 and it said:
27<STOP>
79<STOP>
83<STOP>
So then to press F4 via pexpect:
id.send(chr(27) + chr(79) + chr(83))

Related

Writing string with ANSI escape sequences to file

A python module I am using provides a hook that allows capturing user keyboard input before it is sent to a shell terminal. The problem I am facing is that it captures input character-by-character, which makes capturing the input commands difficult when the user performs such things as backspacing or moving the cursor.
For example, given the string exit\x1b[4D\x1b[Jshow myself out, the following takes place:
>>> a = exit\x1b[4D\x1b[Jshow myself out
>>> print(a)
show myself out
>>> with open('file.txt', 'w+') as f:
>>> f.write(a)
>>> exit()
less abc.txt
The less command shows the raw command (exit\x1b[4D\x1b[Jshow myself out), when in fact I would like it to be stored 'cleanly' as it is displayed when using the print function (show myself out).
Printing the result, or 'cat'ing the file shows exactly what I would want to be displayed, but I am guessing here that the terminal is transforming the output.
Is there a way to achieve a 'clean' write to file, either using some python module, or some bash utility? Surely there must be some module out there that can do this for me?
less is interpreting the control characters.
You can get around this with the -r command line option:
$ less -r file.txt
show myself out
From the manual:
-r or --raw-control-chars
Causes "raw" control characters to be displayed. The default is
to display control characters using the caret notation; for
example, a control-A (octal 001) is displayed as "^A". Warning:
when the -r option is used, less cannot keep track of the actual
appearance of the screen (since this depends on how the screen
responds to each type of control character). Thus, various dis‐
play problems may result, such as long lines being split in the
wrong place.
The raw control characters are sent to the terminal, which then interprets them as cat would.
As others have stated, you would need to interpret the characters yourself before writing them to a file.

Prevent terminal character set switch on data printing

I am running a console application that takes data coming from various sensors around the house. Sometimes the transmission is interrupted and thus the packet does not make sense. When that happens, the contents of the packet is output to a terminal session.
However, what has happened is that while outputting the erroneous packet, it has contained characters that changed character set of the current terminal window, rendering any text (apart from numbers) as unreadable gibberish.
What would be the best way to filter the erroneous packets before their display while retaining most of the special characters? What exactly are sequences that can change behaviour of the terminal?
I would also like to add that apart from scrambled output, the application still works as it should.
Your terminal may be responding to ANSI escape codes.
To prevent the data from inadvertently affecting the terminal, you could print the repr of the data, rather than the data itself:
For example,
good = 'hello'
bad = '\033[41mRED'
print('{!r}'.format(good))
print('{!r}'.format(bad))
yields
'hello'
'\x1b[41mRED'
whereas
print(good)
print(bad)
yields
(Btw, typing reset will reset the terminal.)
You can convert all characters outside of the ASCII range which should eliminate any stray escape sequences that will change the state of your terminal.
print s.encode('string-escape')
When you get a packet check it for validity before outputting it. One possibility is to check that each character in the packet is printable, that is, in the range of 32-127 decimal, before output. Or add checksums to the packets and reject any with bad checksums.

How to send an arrow key use paramiko library in python?

I'm using python 2.7 and code ssh client with paramiko library, I use myhost.channel.send(chr(keycode)) to send every keycode to server. But It only works with 1 byte keycodes. I want to send other multi-byte keycodes like arrow keys. How can I achieve this? Please help me.
A GUI like Windows or MacOS identifies keys with 'keycodes', but an SSH pipe just transfers bytes, not keycodes.
Assuming the program running inside ssh on your server is interactive (that is, it's expecting a human to be using it), you'll need to find out what kind of byte-patterns it's expecting to receive. When you open your channel, make sure you're calling .get_pty() and giving it a terminal parameter (the default, vt100, is pretty safe). Then, you'll need to read the documentation for the VT100 terminal to find out what byte sequences it sends when various keys are pressed. I recommend reading the Xterm Control Sequences documentation (Xterm is not strictly a vt100 emulator, but its documentation is very complete), and not confusingly mixed up with the hardware details of the original VT100 terminal). Note that in that document, "CSI" effectively means the Python string '\e['.
For example, the Xterm Control Sequences document says that the arrow keys are "CSI A" for up, "CSI B" for down, "CSI C" for right, and "CSI D" for left. In Python, that looks like:
up = '\e[A'
down = '\e[B'
right = '\e[C'
left = '\e[D'
In macOS 10.13.2 you can use:
class Keyboard:
up = '\x1b[A'
down = '\x1b[B'
right = '\x1b[C'
left = '\x1b[D'
(I read them from sys.stdin)
I think in python you can do the following:
channel.sendall(chr(0x1b)+"[B")
0x1B is the ASCII Escape character for VT100 terminal.

Can pexpect be told to ignore a pattern or signal?

I'm trying to write a python script to access several Cisco network devices. Sadly the devices are not set up the same, some have banners up that appear at weird places. These banners might have the patterns that have some of the same stuff a prompt might have.
For example, once I log in I expect a prompt of 'hostname#', so I basically use a pexpect('#') I might have to handle certain other prompts such as 'hostname>' as well so pexpect('>') works. I'll really have a list of possible prompts, plus a timeout and act accordingly.
This is fine unless a banner shows up in the middle somewhere. For example, after logging in a banner might show up that says ## Welcome ## and be there right before the prompt. I believe if I could tell pexpect to ignore the regular expression #.*\r I'd be fine, but I'm not sure this is possible. Is it possible to have pexpect ignore a string, or is there a better way to go about this?
A solution I used to use back in the BBS days was to look for a prompt, and then immediately after I got one, look for a line break or a space with a timeout of a second or two. If the second wait timed out, that meant I was really waiting at a prompt. Since pexpect uses regular expressions, you can actually check to see if you receive any character within a second after a prompt.
def wait_for_prompt(session, prompt, timeout=1):
gotprompt = 0
while not gotprompt:
session.expect(prompt, timeout=None)
gotprompt = session.expect([".", pexpect.TIMEOUT], timeout=timeout)
child = pexpect.spawn("ssh ...")
wait_for_prompt(child, "[#>] ?")
I've never actually used this library before so this may need some tweaking, but it's an approach I used successfully in the '80s. :-)
The Python pexpect module waits for matching input. You can't actually ignore input, except by creating regular expressions that will not match ambiguous or undesirable input. Instead of creating one really complex regex, though, pexpect can also handle lists of regular expressions. You then anchor those regular expressions to reduce ambiguity.
For example, you can easily differentiate between a command prompt and a banner by anchoring the match to the start or end of the line, and making the match as exact as possible:
users = ['fred', 'ginger']
user_prompt = '^(?:%s)#.*?\$ $' % '|'.join(users)
child.expect (['hostname# $', 'hostname> $', user_prompt])
They key is to understand what input you want to match, build an unambiguous regular expression with anchors, and then update your expressions whenever you find a mismatch--which hopefully won't be too often because you didn't create overly-greedy patterns in the first place.
Once expect has found a match it sets the 'before', 'after', and 'match' attributes on the process instance. You could use these to write some logic.
Alternatively you could just craft a better regex that only matches the prompt that you want. Even r'>$|#$' would probably do what you want since it is only going to match those characters at the end of a line.

^H ^? in python

Some terminals will send ^? as backspace, some other terminals will send ^H.
Most of the terminals can be configured to change their behavior.
I do not want to deal with all the possible combinations but I would like to accept both ^? and ^H as a backspace from python.
doing this
os.system("stty erase '^?'")
I will accept the first option and with
os.system("stty erase '^H'")
I will accept the second one but the first will be no longer available.
I would like to use
raw_input("userinput>>")
to grab the input.
The only way I was able to figure out is implementing my own shell which works not on "raw based input" but on "char based input".
Any better (and quicker) idea?
The built-in function raw_input() (or input() in Python 3) will automatically use the readline library after importing it. This gives you a nice and full-feautured line editor, and it is probably your best bet on platforms where it is available, as long as you don't mind Readline having a contagious licence (GPL).
I don't know your question exactly. IMO, you need a method to read some line-based text(including some special character) from console to program.
No matter what method you use, if read this character have special mean in different console, you should confront a console(not only system-specific, but also console-specific) question, all text in console will be store in buffer first, and then show in screen, finally processed and send in to your program. Another way to surround this problem is to use a raw line-obtaining console environment.
You can add a special method(a decorator) to decorate the raw_input() or somewhat input method to process special word.
After solved that question
using this snippet can deal with input,:
def pre():
textline=raw_input()
# ^? should replace to the specific value.
textline.replace("^?","^H")
return textline
To be faster, maybe invoke some system function depend on OS is an idea. But in fact, IO in python is faster enough for common jobs.
To fix ^? on erase do stty erase ^H

Categories

Resources