Can pexpect be told to ignore a pattern or signal?

2019-07-31 16:18发布

问题:

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?

回答1:

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 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. :-)



回答2:

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.



回答3:

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.