python3 pexpect奇怪的行为

时间:2018-02-10 00:41:01

标签: python-3.x pexpect

我有一个单线程程序,只需通过ssh执行命令,只需通过ssh查找输出。但过了一段时间,我开始变得非常奇怪的行为:

ssh_cmd = 'ssh %s@%s %s' % (user, addr, options)
ssh = pexpect.spawn(ssh_cmd, timeout=60)
lgsuc = ['(?i)(password)')]
for item in loginsuccess:
    lgsuc.append(item)
retval = ssh.expect(lgsuc)
for cmd in cmdlist:
    time.sleep(0.1)
    #this is regex to match anything. Essentially clears the buffer so you don't get an invalid match from before
ssh.expect(['(?s).*'])
ssh.sendline(cmd)
foundind = ssh.expect([re.escape("root#")], 30) #very slow
#repr escape all the wierd stuff so madness doesn't happen with ctr chars
rettxt = repr(ssh.before.decode("us-ascii") + "root:#")
print("We Found:" + rettxt

大约20个命令就可以了,然后发生疯狂假设每次正确的回声都是blablabla:

We found 'blablabla \r\n\r\n[edit]\r\nroot#'
We found 'blablabla \r\n\r\n[edit]\r\nroot#'
We found 'blablabla \r\n\r\n[edit]\r\nroot#'

...大约20个命令......

We found 'bl\r\nroot#'  # here it just missed part of the string in the middle
We found 'alala\r\nroot#'

这是前一个命令的其余回声!!!并且当前命令的回声将在一段时间之后出现!它变得越来越糟。奇怪的是它位于返回字节数组的中间。 现在有一些奇怪的控制代码从这个设备返回,所以如果我更换:

rettxt = repr(ssh.before.decode("us-ascii") + "root:#")

rettxt = repr(ssh.before.decode("us-ascii") + "root:#")

然后

print("We Found:" + rettxt) 

返回:

root#e Found lala

无论如何,pexpect和缓冲区都有一些奇怪的东西,我无法弄清楚它是什么,所以任何帮助都会受到赞赏。我应该提到我永远不会超时,潜水总是响应。此外,日志文件中" root:#"的总数超过了发送的总行数。

如果我查看并删除所有ctl代码,输出看起来更干净但问题仍然存在,就好像pextect无法正确处理其缓冲区中的ctl coodes。任何帮助表示赞赏

更新最低可验证示例

好的,我已经能够在孤立的ubuntu环境中重新创建问题的一部分。

首先我需要创建4个可以在主机目标上运行的命令,所以把文件放在〜/我在ubuntu中这样做了 〜/ check.py

#!/usr/bin/python3
import time
import io
#abcd^H^H^H^H^MABC
#mybytes = b'\x61\x62\x63\x64\x08\x08\x08\x0D\x41\x42\x43'
#abcdACB
mybytes = b'\x61\x62\x63\x64\x41\x42\x43'
f = open('test.txt', 'wb')
#time.sleep(1)
f.write(mybytes)
print(mybytes.decode('ascii'))
f.close()

〜/ check2.py

#!/usr/bin/python3
import time
import io
#0123^H^H^H^H^MABC
mybytes = b'\x30\x31\x32\x33\x08\x0D\x0D\x08\x08\x08\x08\x0D\x41\x42\x43'
f = open('test2.txt', 'wb')
#time.sleep(0.1)
f.write(mybytes)
print(mybytes.decode('ascii'))
f.close()

〜/ check3.py

#!/usr/bin/python3
import time
import io
#789:^H^H^H^H^DABC
mybytes = b'\x37\x38\x39\x3A\x08\x08\x08\x08\x08\x08\x08\x0D\x0D\x41\x42\x43'
f = open('test3.txt', 'wb')
#time.sleep(0.1)
f.write(mybytes)
print(mybytes.decode('ascii'))
f.close()

最后check4.py抱歉,这个问题需要一个奇怪的组合才能显示出来

#!/usr/bin/python3
import time
import io
#abcd^H^H^H^HABC
mybytes = b'\x61\x62\x63\x64\x08\x08\x08\x0D\x41\x42\x43'
f = open('test.txt', 'wb')
time.sleep(4)
f.write(mybytes)
print(mybytes.decode('ascii'))
f.close()

注意到最后一个有更大的睡眠,这是遇到texpect超时。虽然在我的实际测试中,这并不是一件好事,但我有超过6分钟的命令可以返回任何文本,因此这可能是其中的一部分。好的,最后的文件运行一切。它可能看起来很丑,但我做了大量修剪,所以我可以在这里发布:

#! /usr/bin/python3

#import paramiko
import time
import sys
import xml.etree.ElementTree as ET
import xml
import os.path
import traceback
import re
import datetime
import pexpect
import os
import os.path
ssh = None
respFile = None
#Error Codes:
DEBUG = True
NO_ERROR=0
EXCEPTION_THROWS=1
RETURN_TEXT_NEVER_FOUND = 2

LOG_CONSOLE = 1
LOG_FILE = 2
LOG_BOTH = 3


def log(out, dummy=None):
    print(str(log))


def connect(user, addr, passwd):
    global ssh
    fout = open('session.log', 'wb')
    #options = '-q -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oPubkeyAuthentication=no'
    options = ' -oUserKnownHostsFile=/dev/null '
    #options = ''
    #REPLACE WITH YOU LOCAL USER NAME
    #user = 'user'
    #REPLACE WITH YOUR LOCAL PASSWORD
    #passwd = '123TesT321'
    #addr = '127.0.0.1'

    ssh_cmd = 'ssh %s@%s %s' % (user, addr, options)
    public = None
    private = None

    retval = 0
    try:
        ssh = pexpect.spawn(ssh_cmd, timeout=60)
        ssh.logfile = fout
        #the most common prompts
        loginsuccess = [re.escape("#"), re.escape("$")]
        lgsuc = ['(?i)(password)',  re.escape("connecting (yes/no)? ")]
        for item in loginsuccess:
            lgsuc.append(item)
        retval = ssh.expect(lgsuc)


    except pexpect.TIMEOUT as exc:
        log("Server never connected to SSH tunnel")
        return 0
    print('where here ret val = ' + str(retval))
    try:
        if(retval > 1):
            return 1
        elif(retval == 1):
            hostkey = ssh.before.decode("utf-8")
            ssh.sendline("yes")
            log("Warning! new host key was added to the database: " + hostkey.split("\n")[1])
            lgsuc = ['password: ']
            for item in loginsuccess:
                lgsuc.append(item)
            retval = ssh.expect(lgsuc)
            if(retval > 0):
                return 1
            else:
                if(public is not None):
                    log("Warning public key authentication failed trying password if available...")
        else:
            if public is not None:
                log("Warning public key authentication failed trying password if available...")

        if(passwd is None):
            log("No password and certificate authentication failed...")
            return 0
        ssh.sendline(passwd)
        login = ['password: ' ]
        for item in loginsuccess:
            login.append(item)
        retval = ssh.expect(login)
    except pexpect.TIMEOUT as exc:
        log("Server Never responded with expected login prompt: "+lgstr)
        return 0
        #return 0
    if retval > 0:
        retval = 1
    if retval == 0:
        log("Failed to connect to IP:"+addr +" User:"+user+" Password:"+passwd)

    return retval

def disconnect():
    log("Disconnecting...")
    global ssh
    if ssh is not None:
        ssh.close()
    else:
        log("Something wierd happened with the SSH client while closing the session. Shouldn't really matter", False)


def RunCommand(cmd, waitTXT, timeout = 5):
    global ssh
    Error = 0
    if DEBUG:
        print('Debugging: cmd: '+ cmd+'. timeout: '+str(timeout) +'. len of txt tags: '+ str(len(waitTXT)))
    if(type(waitTXT) is str):
        waitTXT = [re.excape(waitTXT)]
    elif(not hasattr(waitTXT ,'__iter__')):
         waitTXT = [re.escape(str(waitTXT))]
    else:
        cnter = 0
        for TXT in waitTXT:
            waitTXT[cnter] = re.escape(str(TXT))
            cnter +=1
    #start = time.time()
    #print("type3: "+str(type(ssh)))
    #time.sleep(1)
    #this is regex to match anything. Essentially clears the buffer so you don't get an invalid match from before
    ssh.expect(['(?s).*'])

    ssh.sendline(cmd)
    print("Debugging: sent: "+cmd)
    #GoOn = True
    rettxt = ""
    try:
        foundind = ssh.expect(waitTXT, timeout)
        allbytes = ssh.before
        newbytes = bytearray()
        for onebyte in allbytes:
            if onebyte > 31:
                newbytes.append(onebyte)
        allbytes = bytes(newbytes)
        rettxt = repr(allbytes.decode("us-ascii") + waitTXT[foundind])
        #rettxt = ssh.before + waitTXT[foundind]
        if DEBUG:
            print("Debugging: We found "+rettxt)
    except pexpect.TIMEOUT as exc:
        if DEBUG:
            txtret = ""
            for strtxt in waitTXT:
                txtret += strtxt +", "
            print("ERROR Debugging: we timed out waiting for text:"+txtret)
        pass
    return (rettxt, Error)


def CloseAndExit():
    disconnect()
    global respFile
    if respFile is not None and '_io.TextIOWrapper' in str(type(respFile)):
        if not respFile.closed:
            respFile.close()


def main(argv):
    try:
        cmds = ['~/check.py', '~/check2.py', '~/check3.py', '~/check2.py', '~/check3.py','~/check.py', '~/check2.py', '~/check3.py', '~/check2.py', '~/check3.py', '~/check4.py', '~/check3.py','~/check.py', '~/check2.py',]
        ##CHANGE THESE TO MTACH YOUR SSH HOST
        ret = connect('user', '127.0.0.1', 'abcd1234')
        for cmd in cmds:
            cmdtxt = str(cmd)
        #rett = RunCommand(ssh, "ls", "root", 0, 5)
            strlen = (170 - (len(cmdtxt)))/2
            dashval = ''
            starval = ''
            tcnt = 0
            while(tcnt < strlen):
                dashval +='-'
                starval +='*'
                tcnt +=1
            if DEBUG:
                print(dashval+cmdtxt+dashval)
            #checkval = ['ABC']
            #REPLACE THE FOLLOWING LINE WITH YOUR TARGET PROMPT
            checkval = ['user-virtual-machine:~$']
            rett = RunCommand(cmdtxt, checkval,  2)
            if DEBUG:
                print(starval+cmdtxt+starval)

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
        print(exc_type, fname, exc_tb.tb_lineno)
        print(traceback.format_exc())
    CloseAndExit()
    #disconnect()
    #respFile.close()


main(sys.argv)

确保所有检查和主要python脚本都可以通过sudo chmod 774或类似权限执行。在主函数调用中,将您的用户名ipaddress和密码设置为具有check.py的目标所在的位置,并确保它们位于〜/目录中。

一旦你运行它,你可以看看session.log,至少在脑海中有一些奇怪的东西正在进行缓冲:

~/check4.py^M
~/check3.py
~/check3.py^M
abcd^H^H^H^MABC^M
^[]0;user@user-virtual-machine: ~^Guser@user-virtual-machine:~$ ~/check.py
~/check3.py^M
789:^H^H^H^H^H^H^H^M^MABC^M
^[]0;user@user-virtual-machine: ~^Guser@user-virtual-machine:~$ ~/check.py~/check2.py

不幸的是,它不像我的实际prbolem腐败,但我有几百个命令我是一个嵌入式定制linux内核,我显然无法为每个人重新创建。但无论如何,任何帮助都非常感谢。希望这些例子对你有用,我只是在ubuntu 16.04 lts。还要确保更换用户虚拟机:〜$&#39;无论你的目标登录提示是什么样的

0 个答案:

没有答案