在bash脚本中帮助Expect

时间:2011-04-07 03:44:01

标签: bash expect

我写了一个期望内部的bash脚本,连接到终端服务器和清除行。 我无法弄清楚我得到的错误,因为我已经给了所有必要的括号。我也不理解couldn't read file "line": no such file or directory错误。 请帮助。

我的剧本:

#!/bin/bash  
VAR=$(expect -c "  
spawn telnet 1.1.1.1   
expect {  
       "Password:" { send "password\r" ; exp_continue}  
       "Prompt>" { send "en\r" ; exp_continue}  
       "Password:" { send "password\r" ; exp_continue}  
       "Prompt#" {send "clea line 10\r" ; exp_continue}  
       "[confirm]" {send "Y\r" ; exp_continue}  
       "Prompt#" {send "clea line 11\r" ; exp_continue}  
       "[confirm]" {send "Y\r" ; exp_continue}  
       "Prompt#" {send "exit\r" }  
    }  
")  

echo $VAR  

其输出:

missing close-brace  
    while executing  
"expect"  
couldn't read file "line": no such file or directory  

4 个答案:

答案 0 :(得分:20)

第一个问题是shell不会像你想的那样解释嵌套的双引号。解决此问题的最简单方法是将Expect程序放在单引号中。只要Expect程序本身没有单引号,这就足够了。

您将遇到的下一个问题是,在单个expect命令中拥有所有模式和操作将并行处理它们。实际发生的是,第一个Password:模式将在它看到该字符串的任何时间匹配(即,即使是第二次管理密码)。如果两个密码需要不同,这将是一个问题。至少,相同的模式需要进入单独的expect命令,以便它们可以按顺序执行。此问题还会影响您查找三次的Prompt#模式,并希望发送三种不同的响应。

稍后,在发送第一个clear命令后,您将收到错误消息。 Expect以类似于shell解释$()``(即命令替换)的方式解释双引号内的方括号。您将看到如下错误:

invalid command name "confirm"
    while executing
"confirm"
    invoked from within
"expect {  
⋮

尝试将confirm作为Tcl(或Expect)命令运行。您可以使用大括号({})来阻止Tcl进行此解释。此外,期望模式默认被视为“glob”表达式(例如shell通配符),因此即使您将{[confirm]}写为模式,它仍然不会用于精确的字符串匹配(它将匹配任何单个字符confirm)。您必须使用-ex标记来标记模式以进行精确匹配。

解决这些问题,删除一些不必要的引用,最后你可能会得到类似的结果:

#!/bin/sh
VAR=$(expect -c '
  proc abort {} {
    puts "Timeout or EOF\n"
    exit 1
  }
  spawn telnet 1.1.1.1
  expect {
    Password:        { send "password1\r" }
    default          abort
  }
  expect {
    Prompt>          { send "en\r"; exp_continue }
    Password:        { send "password2\r" }
    default          abort
  }
  expect {
    Prompt#          { send "clea line 10\r"; exp_continue }
    -ex {[confirm]}  { send "Y\r" }
    default          abort
  }
  expect {
    Prompt#          { send "clea line 11\r"; exp_continue }
    -ex {[confirm]}  { send "Y\r" }
    default          abort
  }
  expect {
    Prompt#          { send "exit\r"; exp_continue }
    timeout          abort
    eof
  }
  puts "Finished OK\n"
')

echo "$VAR"

答案 1 :(得分:3)

@Chris:我收录了您建议的更改,我的代码现在正在运行。

但是我必须再做两次更改:

1]您提到的单引号会阻止参数替换。例如,我无法用$IP代替1.1.1.1。因此,为了解决这个问题,我删除了单引号并替换为双引号。正如你所提到的嵌套双引号不是由bash解释的,这是真的。因此我将内部双引号重写为

send \"password1\r\" 

这是在内部双引号之前添加反斜杠。这纠正了参数替换的问题。

2]即使我在一个expect命令中放入两个/三个动作,因为它们并行运行我仍面临问题。因此,根据您的建议,我将每个操作都放在单独的expect命令中。像

这样的东西
expect {  
    Prompt>          { send "en\r"; exp_continue }  
}  
expect  {  
    Password:        { send "password2\r" }  
} 

答案 2 :(得分:1)

问题在于,expect脚本中的双引号被视为expect脚本的结尾。尝试将单引号与双引号混合使用:

#!/bin/bash
VAR=$(expect -c '
spawn telnet 1.1.1.1
expect {
"Password:" { send "password\r" ; exp_continue}
"Prompt>" { send "en\r" ; exp_continue}
"Password:" { send "password\r" ; exp_continue}
"Prompt#" {send "clea line 10\r" ; exp_continue}
"[confirm]" {send "Y\r" ; exp_continue}
"Prompt#" {send "clea line 11\r" ; exp_continue}
"[confirm]" {send "Y\r" ; exp_continue}
"Prompt#" {send "exit\r" }
}
')

答案 3 :(得分:1)

@David有正确的答案。

关于你期望的脚本样式的评论:你的expect / send命令是线性的,所以有一个expect块和一堆exp_continue语句似乎令人困惑。写这个会更直接:

VAR=$(expect -c '
  spawn telnet 1.1.1.1
  expect "Password:"
  send "password\r"
  expect "Prompt>"
  send "en\r"
  expect "Password:"
  send "password\r" 
  expect "Prompt#"
  send "clea line 10\r" 
  expect "[confirm]"
  send "Y\r" 
  expect "Prompt#"
  send "clea line 11\r"
  expect "[confirm]"
  send "Y\r" 
  expect "Prompt#"
  send "exit\r" 
  expect eof
')