【问题标题】:Python pexpect - TIMEOUT falls into traceback and exitsPython pexpect - TIMEOUT 进入回溯并退出
【发布时间】:2012-05-05 15:20:31
【问题描述】:

我是 python-pexpect 的新手。在 Tcl/expect 中,当我遇到超时时 - 我会回复消息并退出函数。我尝试使用发布的示例代码尝试类似的响应 http://pexpect.svn.sourceforge.net/viewvc/pexpect/trunk/pexpect/examples/sshls.py?revision=489&view=markup

我基于上面的这段代码——如果我输入了一个伪造的密码,我希望这只是超时,打印“错误!”,然后退出程序。但是当我运行它时 - 进入“回溯输出”(见下文),有人可以帮我让程序打印“错误”并优雅地退出程序。

test@ubuntu:~/scripts$ ./tmout.py 
Hostname: 192.168.26.84
User: root
Password: 
Timeout exceeded in read_nonblocking().
<pexpect.spawn object at 0xb77309cc>
version: 2.3 ($Revision: 399 $)
command: /usr/bin/ssh
args: ['/usr/bin/ssh', '-l', 'root', '192.168.26.84', '/bin/ls', '-l']
searcher: searcher_re:
    0: EOF
buffer (last 100 chars): 
Permission denied, please try again.
root@192.168.26.84's password: 
before (last 100 chars): 
Permission denied, please try again.
root@192.168.26.84's password: 
after: <class 'pexpect.TIMEOUT'>
match: None
match_index: None
exitstatus: None
flag_eof: False
pid: 14997
child_fd: 3
closed: False
timeout: 30
delimiter: <class 'pexpect.EOF'>
logfile: None
logfile_read: None
logfile_send: None
maxread: 2000
ignorecase: False
searchwindowsize: None
delaybeforesend: 0.05
delayafterclose: 0.1
delayafterterminate: 0.1
Traceback (most recent call last):
  File "./tmout.py", line 54, in <module>
    traceback.print_exc()
NameError: name 'traceback' is not defined
test@ubuntu:~/scripts$ 

源代码:

#!/usr/bin/env python

"""This runs 'ls -l' on a remote host using SSH. At the prompts enter hostname,
user, and password.

$Id$
"""

import pexpect
import getpass, os

def ssh_command (user, host, password, command):

    """This runs a command on the remote host. This could also be done with the
pxssh class, but this demonstrates what that class does at a simpler level.
This returns a pexpect.spawn object. This handles the case when you try to
connect to a new host and ssh asks you if you want to accept the public key
fingerprint and continue connecting. """

    ssh_newkey = 'Are you sure you want to continue connecting'
    child = pexpect.spawn('ssh -l %s %s %s'%(user, host, command))
    i = child.expect([pexpect.TIMEOUT, ssh_newkey, 'password: '])
    if i == 0: # Timeout
        print 'ERROR!'
        print 'SSH could not login. Here is what SSH said:'
        print child.before, child.after
        return None
    if i == 1: # SSH does not have the public key. Just accept it.
        child.sendline ('yes')
        child.expect ('password: ')
        i = child.expect([pexpect.TIMEOUT, 'password: '])
        if i == 0: # Timeout
            print 'ERROR!'
            print 'SSH could not login. Here is what SSH said:'
            print child.before, child.after
            return None       
    child.sendline(password)
    return child

def main ():

    host = raw_input('Hostname: ')
    user = raw_input('User: ')
    password = getpass.getpass('Password: ')
    child = ssh_command (user, host, password, '/bin/ls -l')
    child.expect(pexpect.EOF)
    print child.before

if __name__ == '__main__':
    try:
        main()
    except Exception, e:
        print str(e)
        traceback.print_exc()
        os._exit(1)

【问题讨论】:

  • 你能重新编辑你的文本吗?放入:>>> print '\n\x20\x20\x20\x20'.join(your_code.split('\n'))

标签: python python-2.7 pexpect


【解决方案1】:

关于自动 ssh 登录的注意事项:如果您正在考虑使用 (p)expect 自动 ssh 登录,请考虑改用 ssh 密钥。作为奖励,ssh 密钥比密码更安全。它们比在文件系统上存储纯文本密码安全得多,许多用户都希望这样做。如果您使用 ssh 代理,您可以输入一次密码以在本地解锁您的密钥,然后自动使用该密钥在多个主机上进行多次登录。为方便起见,您可以设置或禁用密码到期的时间限制。

首先,你应该导入回溯,所以如果程序中出现意外错误,traceback.print_exc() 将起作用。

import getpass, os, traceback

我还创建了一个die() 函数,以便在程序退出时关闭 ssh。

def die(child, errstr):
    print errstr
    print child.before, child.after
    child.terminate()
    exit(1)

删除这一行,因为在下一行添加正确的命令后,它可能被错误地留在了那里:

        child.expect ('password: ')

正确的行(处理超时)是:

        i = child.expect([pexpect.TIMEOUT, 'password: '])

然后替换这个:

    child.expect(pexpect.EOF)
    print child.before

用这个:

    i = child.expect([pexpect.TIMEOUT, 'Permission denied', pexpect.EOF])
    if i == 0:
        die(child, 'ERROR!\nSSH timed out. Here is what SSH said:')
    elif i == 1:
        die(child, 'ERROR!\nIncorrect password Here is what SSH said:')
    elif i == 2:
        print child.before

此更改将告诉程序检测错误密码,或在有未知输入时优雅地超时,而不是引发异常。请注意,如果您的命令运行时间超过默认超时持续时间,它将在完成之前中止。您需要将timeout=60 之类的内容设置为expect() 的参数才能更改它。

另外,在超时后返回 None 也没有用。相反,调用die(),它将调用exit(1)

        die(child, 'error message')

这是最终代码:

#!/usr/bin/env python

"""This runs 'ls -l' on a remote host using SSH. At the prompts enter hostname,
user, and password.

$Id$
"""

import pexpect
import getpass, os, traceback

def ssh_command (user, host, password, command):

    """This runs a command on the remote host. This could also be done with the
pxssh class, but this demonstrates what that class does at a simpler level.
This returns a pexpect.spawn object. This handles the case when you try to
connect to a new host and ssh asks you if you want to accept the public key
fingerprint and continue connecting. """

    ssh_newkey = 'Are you sure you want to continue connecting'
    child = pexpect.spawn('ssh -l %s %s %s'%(user, host, command))
    i = child.expect([pexpect.TIMEOUT, ssh_newkey, 'password: '])
    if i == 0: # Timeout
        die(child, 'ERROR!\nSSH could not login. Here is what SSH said:')
    if i == 1: # SSH does not have the public key. Just accept it.
        child.sendline ('yes')
        i = child.expect([pexpect.TIMEOUT, 'password: '])
        if i == 0: # Timeout
            die(child, 'ERROR!\nSSH could not login. Here is what SSH said:')
    child.sendline(password)
    return child

def die(child, errstr):
    print errstr
    print child.before, child.after
    child.terminate()
    exit(1)

def main ():

    host = raw_input('Hostname: ')
    user = raw_input('User: ')
    password = getpass.getpass('Password: ')
    child = ssh_command(user, host, password, '/bin/ls -l')

    i = child.expect([pexpect.TIMEOUT, 'Permission denied', pexpect.EOF])
    if i == 0:
        die(child, 'ERROR!\nSSH timed out. Here is what SSH said:')
    elif i == 1:
        die(child, 'ERROR!\nIncorrect password Here is what SSH said:')
    elif i == 2:
        print child.before

if __name__ == '__main__':
    try:
        main()
    except Exception, e:
        print str(e)
        traceback.print_exc()
        os._exit(1)

【讨论】:

    【解决方案2】:

    我建议尝试使用 pexpect 附带的 pxssh 模块/类来自动化 ssh 会话。 这将通过处理这些常见情况来减少您的代码。

    这是从另一个 StackOverflow https://stackoverflow.com/a/6189676/570450 复制的一些示例代码

    import pxssh
    s = pxssh.pxssh()
    if not s.login ('localhost', 'myusername', 'mypassword'):
        print "SSH session failed on login."
        print str(s)
    else:
        print "SSH session login successful"
        s.sendline ('ls -l')
        s.prompt()         # match the prompt
        print s.before     # print everything before the prompt.
        s.logout()
    

    【讨论】:

      猜你喜欢
      • 2012-09-10
      • 1970-01-01
      • 2016-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-14
      • 1970-01-01
      • 2016-02-17
      相关资源
      最近更新 更多