在“expect”中使用条件语句

33

我需要使用 expect 自动登录TELNET会话,但是我需要处理相同用户名的多个密码。

这是我需要创建的流程:

  1. 打开到IP的TELNET会话
  2. 发送用户名
  3. 发送密码
  4. 密码错误?再次发送相同的用户名,然后发送不同的密码
  5. 此时应已成功登录...

值得一提的是,以下是我目前为止所拥有的内容:

#!/usr/bin/expect
spawn telnet 192.168.40.100
expect "login:"
send "spongebob\r"
expect "password:"
send "squarepants\r"
expect "login incorrect" {
  expect "login:"
  send "spongebob\r"
  expect "password:"
  send "rhombuspants\r"
}
expect "prompt\>" {
  send_user "success!\r"
}
send "blah...blah...blah\r"

不用说这个代码是不工作的,也不美观。从我在Google上的探险中发现,expect 似乎是一种黑魔法。提前感谢任何人对此事提供帮助!


我已经在我的回答中添加了更多信息。 - tvanfosson
3个回答

46

我强烈推荐探索Expect这本书给所有的expect程序员--非常有价值。

我已经重写了你的代码:(未经测试)

proc login {user pass} {
    expect "login:"
    send "$user\r"
    expect "password:"
    send "$pass\r"
}

set username spongebob 
set passwords {squarepants rhombuspants}
set index 0

spawn telnet 192.168.40.100
login $username [lindex $passwords $index]
expect {
    "login incorrect" {
        send_user "failed with $username:[lindex $passwords $index]\n"
        incr index
        if {$index == [llength $passwords]} {
            error "ran out of possible passwords"
        }
        login $username [lindex $passwords $index]
        exp_continue
    }
    "prompt>" 
}
send_user "success!\n"
# ...

exp_continue会回到expect块的开头,就像"redo"语句一样。

请注意,send_user\n而不是\r结尾。

你不需要转义提示符中的>字符:在Tcl中它不是特殊字符。


13

我经过一番研究,找到了解决方案。原来 expect 使用了 TCL 的语法,而我对这个语法并不熟悉:

#!/usr/bin/expect
set pass(0) "squarepants"
set pass(1) "rhombuspants"
set pass(2) "trapezoidpants"
set count 0
set prompt "> "
spawn telnet 192.168.40.100
expect {
  "$prompt" {
    send_user "successfully logged in!\r"
  }
  "password:" {
    send "$pass($count)\r"
    exp_continue
  }
  "login incorrect" {
    incr count
    exp_continue
  }
  "username:" {
    send "spongebob\r"
    exp_continue
  }
}
send "command1\r"
expect "$prompt"
send "command2\r"
expect "$prompt"
send "exit\r"
expect eof
exit

希望这对其他人有用。


1
我曾使用类似的解决方案来处理SSH,但是从密码文件“/.ssh/ssh-passwords”中读取密码。主机名和用户名则从我的“/.ssh/config”文件中读取。因此,运行命令:“cssh server1”将会扩展为“ubuntu@server1.mycompany.com”。 - DavidGamba
我对Expect还不熟悉,expect { "case1"{...} "case2"{...} "case3"{...}} 这个语法是类似于C++的switch case语句吗?谢谢。 - B.Mr.W.
@B.Mr.W. 是的,将其用于外部的 {} 基本上使其成为一个 fall through,而不是单独的 if 语句。 - eulerworks

-1
如果您知道用户ID和密码,那么您也应该知道哪些用户ID/密码对应哪些系统。我认为最好维护一个映射表,记录每个用户ID/密码对应哪个系统,然后提取这些信息并使用正确的用户ID/密码。
既然您显然不喜欢我的建议,那么我建议您查看wikipedia page并实现一个过程,如果成功返回0,如果超时则返回1。这将允许您检测到提供的密码失败--提示超时--并重试。如果这有帮助,您可以删除您的负评,因为我已经编辑了它。
回顾一下,您可能需要与映射表一起执行此操作,因为如果更改了密码,您需要检测登录是否失败。

抱歉给你点了踩。我正在进行一次长时间的电话,想要一个快速的方式来表明我还没有答案,而不必打字解释原因。无论如何,如果我有一种可靠的方法来记录某些设备上使用的密码,我肯定不会使用条件方法。它可能会在我无法控制的固件版本之间发生变化,所以我只能准备好一份密码列表来尝试。我知道我需要做什么——使用条件语句并根据答案采取行动——我只是不知道该怎么做。 :) - shuckster
现在我已经编辑过了,你可以删除投票——如果额外的信息有帮助或者至少不会使帖子变得无用。只要你不接受答案,人们很可能会继续添加答案——尽管你可能想更新你的问题,说明为什么难以跟踪密码。 - tvanfosson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接