ターミナルサーバーに接続して回線をクリアするために、expectwithinを使用して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
最初の問題は、シェルがネストされた二重引用符を希望どおりに解釈しないことです。これを修正する最も簡単な方法は、Expectプログラムを一重引用符で囲むことです。 Expectプログラム自体に一重引用符がない限り、これで十分です。
次に遭遇する問題は、1つのexpect
コマンドにすべてのパターンとアクションを含めると、それらが並行して処理されることです。実際に発生するのは、最初の_Password:
_パターンが、その文字列を検出するたびに一致することです(つまり、2回目の管理者パスワードの場合でも)。これは、2つのパスワードを異ならせる必要がある場合に問題になります。少なくとも、同じパターンを別々のexpect
コマンドに入れて、順番に実行できるようにする必要があります。この問題は、3回検索して3つの異なる応答を送信する_Prompt#
_パターンにも影響します。
後で、最初のclearコマンドを送信した後、エラーが発生します。 Expectは、シェルが$()
または_``
_を解釈する方法(つまり、コマンド置換)と同様の方法で、二重引用符内の角括弧を解釈します。次のようなエラーが表示されます。
_invalid command name "confirm"
while executing
"confirm"
invoked from within
"expect {
⋮
_
confirm
をTcl(またはExpect)コマンドとして実行しようとしています。中括弧(_{}
_)を使用して、Tclがこの解釈を行わないようにすることができます。さらに、expectパターンはデフォルトで「glob」式として扱われるため(つまり、シェルワイルドカードのように)、パターンとして_{[confirm]}
_を記述しても、完全な文字列一致には使用されません(単一文字c
、o
、n
、f
、i
、r
、またはm
)。完全に一致するようにパターンをマークするには、_-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"
_
@Chris:あなたが提案した変更を取り入れて、私のコードは現在機能しています。
ただし、以下に示す2つの変更を加える必要がありました。
1]あなたが言及した一重引用符は、パラメーターの置換を防ぎます。たとえば、$IP
の代わりに1.1.1.1
を書くことはできません。したがって、これを回避するために、一重引用符を削除し、二重引用符に置き換えました。あなたが言ったように、ネストされた二重引用符は本当のbashによって解釈されません。したがって、内側の二重引用符を次のように書き直しました。
send \"password1\r\"
これは、内部の二重引用符の前に円記号を追加することです。これにより、パラメータ置換の問題が修正されます。
2] 1つのexpectコマンド内に2つまたは3つのアクションを配置した後でも、それらは並行して実行されるため、問題に直面しました。だからあなたの提案を受けて、私はそれぞれのアクションを別々のexpectコマンドに入れました。何かのようなもの
expect {
Prompt> { send "en\r"; exp_continue }
}
expect {
Password: { send "password2\r" }
}
問題は、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" }
}
')
@Davidは正しい答えを持っています。
期待スクリプトスタイルについてのコメント:期待/送信コマンドは線形であるため、1つの期待ブロックと一連の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
')