在Bash脚本中预期

在Bash脚本中预期,bash,expect,Bash,Expect,我用Expect编写了一个Bash脚本,用于连接到终端服务器并清除线路 我无法找出我得到的错误,因为我已经给了所有必要的大括号。我也不理解无法读取文件“行”:没有这样的文件或目录错误。 我怎样才能解决这个问题 我的剧本: #/bin/bash VAR=$(预期为-c) 繁殖telnet 1.1.1.1 期待{ “密码:{send”Password\r;exp_continue} “提示>”{send“en\r;exp_continue} “密码:{send”Password\r;exp_cont

我用Expect编写了一个Bash脚本,用于连接到终端服务器并清除线路

我无法找出我得到的错误,因为我已经给了所有必要的大括号。我也不理解
无法读取文件“行”:没有这样的文件或目录
错误。 我怎样才能解决这个问题

我的剧本:

#/bin/bash
VAR=$(预期为-c)
繁殖telnet 1.1.1.1
期待{
“密码:{send”Password\r;exp_continue}
“提示>”{send“en\r;exp_continue}
“密码:{send”Password\r;exp_continue}
“提示#”{send“clea第10行\r;exp#u continue}
“[确认]”{send“Y\r”;exp\u continue}
“提示#”{send“clea第11行\r;exp#u continue}
“[确认]”{send“Y\r”;exp\u continue}
“提示符#”{send“exit\r”}
}
")
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" }
}
')

第一个问题是shell没有按照您可能喜欢的方式解释嵌套的双引号。解决这个问题的最简单方法是将Expect程序放在单引号中。只要Expect程序本身没有单引号,这就足够了

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

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

invalid command name "confirm"
    while executing
"confirm"
    invoked from within
"expect {  
⋮
#!/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"
它正试图以Tcl(或Expect)命令的形式运行
confirm
。您可以使用花括号(
{}
)来阻止Tcl进行这种解释。此外,expect模式在默认情况下被视为“glob”表达式(例如,与shell通配符类似),因此即使您将
{[confirm]}
作为模式编写,它仍然不会用于精确的字符串匹配(它将匹配任何单个字符
c
o
n
f
i
r
,或
m
)。必须使用
-ex
标志来标记模式以进行精确匹配

解决这些问题,删除一些不必要的引用,您可能会得到如下结果:

invalid command name "confirm"
    while executing
"confirm"
    invoked from within
"expect {  
⋮
#!/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"

@克里斯:我合并了你建议的更改,我的代码现在正在运行

然而,我不得不做出以下两项变更:

  • 您提到的单引号阻止了参数替换。例如,我无法编写
    $IP
    来代替
    1.1.1
    。因此,为了避免这个问题,我删除了单引号,并用双引号替换。正如您提到的,嵌套的双引号不会被Bash解释,这是真的。因此,我重写了inside双引号作为

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

  • 即使在我将两个/三个操作放在一个expect命令中,由于它们并行运行,我仍然面临问题。因此,根据您的建议,我将每个操作放在一个单独的expect命令中。类似于:

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

  • 谢谢你的详细解释。这确实帮助我解决了我的问题。我在同一篇文章的单独回答中又做了两个更改。关于你的Expect脚本风格的评论:你的Expect/send命令是线性的,所以有一个Expect块和一堆exp_continue语句似乎会让人困惑。这会更难理解ghtforward编写此代码:
    VAR=$(expect-c'spawn telnet 1.1.1.1 expect“Password:”send“Password”\r“expect”提示符>”send“en\r”expect“Password:”send“Password\r”expect“Prompt”;”send“clea”line 10\r“expect”[确认]”send“Y\r”expect“Prompt”send“clea”line 11\r“expect”[确认]”send“Y\r”expect“Prompt#”send“exit\r“expect eof”)