当 运行 proc inside proc 时,expect 失败
expect fails when running proc inside proc
我的脚本在使用一个过程时工作正常(检索 sftp 提示)。但是当我尝试在 proc 中使用 proc 时,脚本卡住了,我不知道为什么。
请不要重构代码,那不是重点,我需要了解这里的问题所在。
工作代码:
proc sftp_connect {} {
set times 0;
set connection_retry 2
set timeout 1;
while { $times < $connection_retry } {
spawn sftp ${SFTP_USER}@${SFTP_SERVER}
expect {
timeout { puts "Connection timeout"; exit 1}
default {exit 2}
"*assword:*" {
send "${SFTP_PASSWORD}\n";
expect {
"sftp>" { puts "Connected"; set times [ expr $times+1]; exp_continue}
}
}
}
}
send "quit\r";
}
sftp_connect
调试输出:
expect: does "\r\nsftp> " (spawn_id exp5) match glob pattern "sftp>"? yes
但是在将发送密码移动到单独的 proc 之后,expect 不再检索 sftp 提示符 ("sftp>"):
proc sftp_send_password {} {
send "${SFTP_PASSWORD}\n";
expect {
"sftp>" { puts "Connected"; set times [ expr $times+1]; exp_continue}
}
}
proc sftp_connect {} {
set times 0;
set connection_retry 2
set timeout 1;
while { $times < $connection_retry } {
spawn sftp ${SFTP_USER}@${SFTP_SERVER}
expect {
timeout { puts "Connection timeout"; exit 1}
default {exit 2}
"*assword:*" { sftp_send_password }
}
}
send "quit\r";
}
sftp_connect
调试输出:
expect: does "" (spawn_id exp0) match glob pattern "sftp>"? yes
我手边没有 "Exploring Expect" 的副本,但我认为您 运行 遇到了变量范围问题。 spawn
不可见地设置了一个名为 spawn_id
的变量。当您在 proc 中调用 spawn 时,该变量的范围仅限于该 proc。将其声明为全局:
proc sftp_connect {} {
global spawn_id
# ... rest is the same
}
我认为您不必在 sftp_send_password
中做同样的事情,因为 expect 具有比 Tcl 更宽容的范围方案(如果 expect 没有找到局部变量,请在全局名称空间中查找)。
您的 sftp_send_password
proc 不会影响 sftp_connect
中的 times
变量,但由于相同的变量作用域问题。我会推荐
proc sftp_send_password {times_var} {
upvar 1 $times_var times ;# link this var to that in the caller
send "${SFTP_PASSWORD}\n";
expect {
"sftp>" { puts "Connected"; incr times; exp_continue}
}
# note use of `incr` instead of `expr`
}
然后 sftp_connect
过程发送 times
变量 name:
sftp_send_password times
以下内容来自 expect
的手册页:
Expect takes a rather liberal view of scoping. In particular, variables read by commands specific to the Expect program will be sought first from
the local scope, and if not found, in the global scope. For example, this obviates the need to place global timeout
in every procedure you write
that uses expect. On the other hand, variables written are always in the local scope (unless a global
command has been issued). The most common
problem this causes is when spawn
is executed in a procedure. Outside the procedure, spawn_id
no longer exists, so the spawned process is no longer
accessible simply because of scoping. Add a global spawn_id
to such a procedure.
我的脚本在使用一个过程时工作正常(检索 sftp 提示)。但是当我尝试在 proc 中使用 proc 时,脚本卡住了,我不知道为什么。
请不要重构代码,那不是重点,我需要了解这里的问题所在。
工作代码:
proc sftp_connect {} {
set times 0;
set connection_retry 2
set timeout 1;
while { $times < $connection_retry } {
spawn sftp ${SFTP_USER}@${SFTP_SERVER}
expect {
timeout { puts "Connection timeout"; exit 1}
default {exit 2}
"*assword:*" {
send "${SFTP_PASSWORD}\n";
expect {
"sftp>" { puts "Connected"; set times [ expr $times+1]; exp_continue}
}
}
}
}
send "quit\r";
}
sftp_connect
调试输出:
expect: does "\r\nsftp> " (spawn_id exp5) match glob pattern "sftp>"? yes
但是在将发送密码移动到单独的 proc 之后,expect 不再检索 sftp 提示符 ("sftp>"):
proc sftp_send_password {} {
send "${SFTP_PASSWORD}\n";
expect {
"sftp>" { puts "Connected"; set times [ expr $times+1]; exp_continue}
}
}
proc sftp_connect {} {
set times 0;
set connection_retry 2
set timeout 1;
while { $times < $connection_retry } {
spawn sftp ${SFTP_USER}@${SFTP_SERVER}
expect {
timeout { puts "Connection timeout"; exit 1}
default {exit 2}
"*assword:*" { sftp_send_password }
}
}
send "quit\r";
}
sftp_connect
调试输出:
expect: does "" (spawn_id exp0) match glob pattern "sftp>"? yes
我手边没有 "Exploring Expect" 的副本,但我认为您 运行 遇到了变量范围问题。 spawn
不可见地设置了一个名为 spawn_id
的变量。当您在 proc 中调用 spawn 时,该变量的范围仅限于该 proc。将其声明为全局:
proc sftp_connect {} {
global spawn_id
# ... rest is the same
}
我认为您不必在 sftp_send_password
中做同样的事情,因为 expect 具有比 Tcl 更宽容的范围方案(如果 expect 没有找到局部变量,请在全局名称空间中查找)。
您的 sftp_send_password
proc 不会影响 sftp_connect
中的 times
变量,但由于相同的变量作用域问题。我会推荐
proc sftp_send_password {times_var} {
upvar 1 $times_var times ;# link this var to that in the caller
send "${SFTP_PASSWORD}\n";
expect {
"sftp>" { puts "Connected"; incr times; exp_continue}
}
# note use of `incr` instead of `expr`
}
然后 sftp_connect
过程发送 times
变量 name:
sftp_send_password times
以下内容来自 expect
的手册页:
Expect takes a rather liberal view of scoping. In particular, variables read by commands specific to the Expect program will be sought first from the local scope, and if not found, in the global scope. For example, this obviates the need to place
global timeout
in every procedure you write that uses expect. On the other hand, variables written are always in the local scope (unless aglobal
command has been issued). The most common problem this causes is whenspawn
is executed in a procedure. Outside the procedure,spawn_id
no longer exists, so the spawned process is no longer accessible simply because of scoping. Add aglobal spawn_id
to such a procedure.