简体   繁体   English

输出不同步之前的Python / Pexpect

[英]Python / Pexpect before output out of sync

I'm using Python/Pexpect to spawn an SSH session to multiple routers. 我正在使用Python / Pexpect生成到多个路由器的SSH会话。 The code will work for one router but then the output of session.before will get out of sync with some routers so that it will return the output from a previous sendline. 该代码将适用于一个路由器,但是session.before的输出将与某些路由器不同步,因此它将返回前一个sendline的输出。 This seems particularly the case when sending a blank line (sendline()). 发送空白行(sendline())时尤其如此。 Anyone got any ideas? 任何人有任何想法吗? Any insight would be really appreciated. 任何见解将不胜感激。

Below is a sample of what I'm seeing: 以下是我所看到的示例:

ssh_session.sendline('sh version')
while (iresult==2):
    iresult = ssh_session.expect(['>','#','--More--'],timeout=SESSION_TIMEOUT)
    debug_print("execute_1 " + str(iresult))
    debug_print("execute_bef " + ssh_session.before)
    debug_print("execute_af " + ssh_session.after)

    thisoutput = ssh_session.before
    output += thisoutput

    if(iresult==2):
        debug_print("exec MORE")
        ssh_session.send(" ")
    else:
        debug_print("exec: end loop")

for cmd in config_commands:
    debug_print("------------------------------------------------\n")
    debug_print ("running command " + cmd.strip() + "\n")
    iresult=2
    ssh_session.sendline(cmd.strip())
    while (iresult==2):
        iresult = ssh_session.expect([prompt+">",prompt+"#"," --More-- "],timeout=SESSION_TIMEOUT)
        thisoutput = ssh_session.before
        debug_print("execute_1 " + str(iresult))
        debug_print("execute_af " + ssh_session.after)
        debug_print("execute_bef " + thisoutput)
        thisoutput = ssh_session.before
        output += thisoutput

        if(iresult==2):
           debug_print("exec MORE")
           ssh_session.send(" ")
        else:
           debug_print("exec: end loop")


I get this:

logged in
exec: sh version
execute_1 1
execute_bef 
R9
execute_af #
exec: end loop
------------------------------------------------

running command config t

execute_1 1
execute_af #
execute_bef sh version
Cisco IOS Software, 1841 Software (C1841-IPBASEK9-M), Version 15.1(4)M4, RELEASE SOFTWARE (fc1)
Technical Support: http://www.cisco.com/techsupport...

I've run into this before with pexpect (and I'm trying to remember how I worked around it). 我以前曾以pexpect遇到过这个问题(并且我想记住自己是如何工作的)。

You can re-synchronize with the terminal session by sending a return and then expecting for the prompt in a loop. 您可以通过发送返回值,然后在循环中期待提示来与终端会话重新同步。 When the expect times out then you know that you are synchronized. 当期望超时时,您就知道自己已同步。

The root cause is probably that you are either: 根本原因可能是您是:

  • Calling send without a match expect (because you don't care about the output) 调用send时没有预期的匹配(因为您不关心输出)

  • Running a command that produces output but expecting for a pattern in the middle of that output and then not to next prompt that is at end of the output. 运行一个产生输出的命令,但期望在该输出的中间有一个模式,然后不要到输出末尾的下一个提示符。 One way to deal with this is to change your expect pattern to "(.+)PROMPT" - this will expect until the next prompt and capture all the output of the command sent (which you can parse in the next step). 解决此问题的一种方法是将您的期望模式更改为“(。+)PROMPT”-这将一直等到下一个提示符并捕获已发送命令的所有输出(您可以在下一步中进行分析)。

I faced a similar problem. 我遇到了类似的问题。 I tried waiting for the command to be printed on the screen and the sending enter. 我尝试等待命令在屏幕上打印并发送回车。

I you want to execute say command 'cmd', then you do: 我要执行命令“ cmd”,然后执行以下操作:

    session.send(cmd)
    index = session.expect([cmd, pexpect.TIMEOUT], 1)
    session.send('\n')
    index = session.expect([whatever you expect])

Worked for me. 为我工作。

I'm not sure this is the root of your problem, but it may be worth a try. 我不确定这是否是您问题的根源,但可能值得尝试。

Something I've run into is that when you spawn a session that starts with or lands you in a shell, you have to deal with quirks of the TERM type (vt220, color-xterm, etc.). 我遇到的一个问题是,当您生成一个以shell开头或登陆您的会话时,您必须处理TERM类型(vt220,color-xterm等)的怪癖。 You will see characters used to move the cursor or change colors. 您将看到用于移动光标或更改颜色的字符。 The problem is almost guaranteed to show up with the prompt; 几乎可以保证在提示中出现问题。 the string you are looking for to identify the prompt appears twice because of how color changes are handled (the prompt is sent, then codes to backspace, change the color, then the prompt is sent again... but expect sees both instances of the prompt). 由于要处理颜色更改,您正在寻找的用于标识提示的字符串出现了两次(发送提示,然后编码到退格键,更改颜色,然后再次发送提示...但是希望看到两个实例提示)。

Here's something that handles this, guaranteed to be ugly, hacky, not very Pythonic, and functional: 以下是处理此问题的方法,保证它们丑陋,hacky,不是非常Pythonic且功能强大:

import pexpect

# wait_for_prompt: handle terminal prompt craziness
#   returns either the pexpect.before contents that occurred before the 
#   first sighting of the prompt, or returns False if we had a timeout
#
def wait_for_prompt(session, wait_for_this, wait_timeout=30):
    status = session.expect([wait_for_this, pexpect.TIMEOUT, pexpect.EOF], timeout=wait_timeout)
    if status != 0:
        print 'ERROR : timeout waiting for "' + wait_for_this + '"'
        return False
    before = session.before # this is what we will want to return
    # now look for and handle any additional sightings of the prompt
    while True:
        try:
            session.expect(wait_for_this, timeout=0.1)
        except:
            # we expect a timeout here. All is normal. Move along, Citizen.
            break # get out of the while loop
        return before

s = pexpect.spawn('ssh me@myserver.local')
s.expect('password') # yes, we assume that the SSH key is already there
                     # and that we will successfully connect. I'm bad.
s.sendline('mypasswordisverysecure') # Also assuming the right password
prompt = 'me$'
wait_for_prompt(s, prompt)
s.sendline('df -h') # how full are my disks?
results = wait_for_prompt(s, prompt)
if results:
    print results
    sys.exit(0)
else:
    print 'Misery. You lose.'
    sys.exit(1)

I know this is an old thread, but I didn't find much about this online and I just got through making my own quick-and-dirty workaround for this. 我知道这是一个旧线程,但是在网上并没有太多了解,我只是为此做出了自己的快速变通方法。 I'm also using pexpect to run through a list of network devices and record statistics and so forth, and my pexpect.spawn.before will also get out of sync sometimes. 我还使用pexpect来遍历网络设备列表并记录统计信息等等,并且我的pexpect.spawn.before有时也会不同步。 This happens very often on the faster, more modern devices for some reason. 由于某种原因,这种情况经常发生在更快,更现代的设备上。

My solution was to write an empty carriage return between each command, and check the len() of the .before variable. 我的解决方案是在每个命令之间编写一个空回车,并检查.before变量的len()。 If it's too small, it means it only captured the prompt, which means it must be at least one command behind the actual ssh session. 如果它太小,则意味着它仅捕获了提示,这意味着它必须在实际ssh会话之后至少有一个命令。 If that's the case, the program sends another empty line to move the actual data that I want into the .before variable: 如果是这样,该程序将发送另一个空行,以将所需的实际数据移动到.before变量中:

def new_line(this, iteration):
    if iteration > 4:
        return data
    else:
        iteration+=1
        this.expect(":")
        this.sendline(" \r")
        data = this.before
        if len(data) < 50:
        # The numer 50 was chosen because it should be longer than just the hostname and prompt of the device, but shorter than any actual output
            data = new_line(this, iteration)
        return data

def login(hostname):
    this = pexpect.spawn("ssh %s" % hostname)
    stop = this.expect([pexpect.TIMEOUT,pexpect.EOF,":"], timeout=20)
    if stop == 2:
        try:
            this.sendline("\r")
            this.expect(":")
            this.sendline("show version\r")
            version = new_line(this,0)
            this.expect(":")
            this.sendline("quit\r")
            return version
        except:
            print 'failed to execute commands'
            this.kill(0)
    else:
        print 'failed to login'
        this.kill(0)

I accomplish this by a recursive command that will call itself until the .before variable finally captures the command's output, or until it calls itself 5 times, in which case it simply gives up. 我通过递归命令完成此操作,该命令将自行调用,直到.before变量最终捕获命令的输出为止,或者直到它自行调用5次为止,在这种情况下,它只是放弃。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM