4

私は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)
4

2 に答える 2

9

ssh ログインの自動化に関する注意: ssh ログインの自動化に (p)expect を使用することを考えている場合は、代わりに ssh キーを使用することを検討してください。おまけとして、ssh キーはパスワードよりも安全です。ファイル システムにプレーン テキストのパスワードを保存するよりもはるかに安全です。多くのユーザーがそうしています。ssh エージェントを使用する場合は、パスワードを 1 回入力してキーをローカルでロック解除し、そのキーを複数のホストでの複数のログインに自動的に使用できます。さらに便利なように、パスワードの有効期限を設定または無効にすることができます。

まず、プログラムに予期しないエラーが発生した場合に機能するように、トレースバックをインポートする必要があります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()which を呼び出します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)
于 2012-05-13T15:39:46.337 に答える
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()
于 2013-03-21T16:00:57.423 に答える