繁体   English   中英

在 Bash 脚本中期望

[英]Expect within a Bash script

我用 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

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

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

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

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

它试图将confirm作为 Tcl(或 Expect)命令运行。 您可以使用大括号 ( {} ) 来防止 Tcl 进行这种解释。 此外,expect 模式默认被视为“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"

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

@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" } }

@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
')

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM