在 bash 脚本中帮助使用 Expect

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5575702/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 20:22:52  来源:igfitidea点击:

Help with Expect within a bash script

bashexpect

提问by Pkp

I wrote a bash script with expect within, to connect to a terminal server and clear lines. I am unable to figure out the error I am getting, as in I have given all the braces necessary. I also do not understanding the couldn't read file "line": no such file or directoryerror. Kindly help.

我写了一个带有 expect 的 bash 脚本,以连接到终端服务器并清除线路。我无法弄清楚我得到的错误,因为我已经提供了所有必要的括号。我也不明白这个couldn't read file "line": no such file or directory错误。请帮忙。

My script:

我的脚本:

#!/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  

Its output:

它的输出:

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

回答by Chris Johnsen

The first problem is that the shell does not interpret nested double quotes as you might like. The easiest way to fix this is to put the Expect program in single quotes. This will be sufficient as long as there are no single quotes in the Expect program itself.

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

The next problem you will run into is that having all the patterns and actions in a single expectcommand will process them in parallel. What is actually happens is that the first Password:pattern will match any time it sees that string (i.e. even for the admin password the second time around). This will be a problem if the two passwords need to be different. At a minimum, identical patterns will need to go into separate expectcommands so that they can be executed sequentially. This problem also affects the Prompt#pattern where you look for it three times and want to send three different responses.

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

Later, you will get an error after you send the first clear command. Expect interprets square brackets inside double quotes in a way that is similar to how shells interpret $()or ``(i.e. command substitution). You will see an error like this:

稍后,您在发送第一个清除命令后会出现错误。Expect以类似于shell解释$()``(即命令替换)的方式解释双引号内的方括号。你会看到这样的错误:

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

It is trying to run confirmas a Tcl (or Expect) command. You can use curly brackets ({}) to prevent Tcl from making this interpretation. Furthermore, expect patterns are treated as “glob” expressions by default (i.e. like shell wildcards), so even if you write {[confirm]}as the pattern, it will still not be used for an exact string match (it would match any single character c, o, n, f, i, r, or m). You must use the -exflag to mark the pattern for exact matching.

它试图confirm作为 Tcl(或 Expect)命令运行。您可以使用大括号 ( {}) 来防止 Tcl 进行这种解释。此外,默认情况下,expect 模式被视为“glob”表达式(即像 shell 通配符),所以即使你写成{[confirm]}模式,它仍然不会用于精确的字符串匹配(它将匹配任何单个字符c, o, n, fir、 或m)。您必须使用-ex标志来标记模式以进行精确匹配。

Fix these issues, drop some of the unnecessary quoting, and you might end up with something like this:

解决这些问题,去掉一些不必要的引用,你可能会得到这样的结果:

#!/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"

回答by Pkp

@Chris: I incorporated the changes you suggested and my code is working now.

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

However I had to make two more changes stated below:

但是,我不得不进行以下两项更改:

1] The single quote which you mentioned prevents parameter substitution. For example I cannot write $IPin place of 1.1.1.1. Hence to get around this I removed the single quotes and replaced with double quotes. As you mentioned nested doubles quotes are not interpreted by bash which is true. Hence I rewrote the inside double quotes as

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

send \"password1\r\" 

That is adding backslash before the double quotes inside. This corrects the problem of parameter substitution.

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

2] Even after I put two/three actions within a single expect command, as they run in parallel I stil faced issues. So taking your suggestion I put each of the action in separate expect command. Something like

2] 即使在我将两个/三个动作放在单个期望命令中之后,当它们并行运行时,我仍然遇到问题。因此,根据您的建议,我将每个操作放在单独的 expect 命令中。就像是

expect {  
    Prompt>          { send "en\r"; exp_continue }  
}  
expect  {  
    Password:        { send "password2\r" }  
} 

回答by David

The problem is that that the double quotes in the expect script are treated as the end of the expect script. Try mixing single quotes with double quotes:

问题在于,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" }
}
')

回答by glenn Hymanman

@David has the right answer.

@David 有正确的答案。

A comment about your expect script style: your expect/send commands are linear, so it seems confusing to have one expect block and a bunch of exp_continue statements. It would be more straightforward to write this:

关于您的期望脚本风格的评论:您的期望/发送命令是线性的,因此拥有一个期望块和一堆 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
')