2011-04-07 100 views
10

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

我的脚本:

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

回答

20

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

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

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

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

它试图运行confirm作为一个Tcl(或期望)命令。您可以使用大括号({})来防止Tcl做出这种解释。此外,预期模式在默认情况下被视为“glob”表达式(即与shell通配符一样),所以即使您将{[confirm]}作为模式编写,它仍不会用于精确的字符串匹配(它将匹配任何单个字符co,n,f,i,rm)。您必须使用-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" 
+0

感谢您的详细解释。这真的帮助我解决我的问题,我做了两个更多的更改,我将在单独回答相同的帖子时详细说明。 – Pkp 2011-04-07 18:45:53

1

的问题是,在预期脚本双引号的期望脚本的结尾处理。尝试用双引号混合单引号:

#!/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" } 
} 
') 
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 
') 
3

@克里斯:我纳入你建议的修改,现在我的代码工作。

但是我不得不做出两个如下所述的变化:

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