expect内部循环计数器不递增导致bash脚本死循环问题
死循环的根本原因
- 变量解析顺序完全错位:使用无引号标记的here-doc时,bash会在将内容传给expect之前,预先解析块内所有
$开头的表达式,把它们替换为bash侧的硬编码值。你在expect循环里写的判断条件while {$counter2 < $file_size }中的$counter2,在bash阶段就被替换成了最开头定义的常量1,expect拿到的实际循环条件是固定值对比,不存在可变的循环计数器。你测试时看到的固定计数器输出,就是因为send $counter2这句代码也被bash提前替换成了硬编码的send 1,expect根本没有机会把内部维护的动态计数器值发出来。 - 循环内的递增逻辑被篡改:expect中用来做计数器自增的
set counter2 [ expr $counter2 + 1],内部的$counter2同样被bash提前替换为硬编码的1,expect拿到的实际逻辑是每轮循环都把counter2赋值为固定的2,完全没有按执行次数累加的效果,自然永远达不到退出条件,触发死循环。 - 双语法混用未做隔离:expect基于Tcl语法定义自己的变量规则,和bash的变量语法并不兼容。你直接将bash的变量透传逻辑套用到expect块中,没有对expect需要自行解析的变量做转义/隔离,最终执行的代码逻辑和你预期的完全不一致。
修正方案
- 给here-doc的结束标记加单引号,关闭bash对expect块的预先解析。将
expect << END_EXPECT修改为expect << 'END_EXPECT',保证块内的expect代码原封不动传给解释器,不会被bash提前替换内容。 - 跨层传入的bash变量需要做显式映射:所有需要从bash侧带入expect的值(比如ssh目标地址
$1、文件大小$file_size、xpath数组、初始计数器值),不能靠直接写$xxx让bash替换,要在expect块开头通过命令行参数或者占位符替换的方式,显式声明为expect内部变量。 - 修正expect语法细节:
- 发送命令的字符串需要用双引号包裹,单引号在Tcl/expect中不会处理转义,比如原来的
send 'quit\r'会把单引号本身也作为发送内容,应修改为send "quit\r" - expect内部的计数器要从初始值开始自增,不能依赖bash侧的硬编码值
修正后的核心逻辑参考:
# 外层bash定义传入expect的参数 target_host=$1 expect $target_host $file_size ${xpath[*]} << 'EXPECT_EOF' set timeout 60 set counter2 1 # 从expect的参数列表读取bash传入的值 set target_host [lindex $argv 0] set file_size [lindex $argv 1] set xpath_list [lrange $argv 2 end] spawn ssh user@$target_host expect "user@*>" while {$counter2 < $file_size} { send "\r" expect "user@*>" send "[lindex $xpath_list [expr $counter2 - 1]]\r" set counter2 [expr $counter2 + 1] send "$counter2" } send "quit\r" expect eof EXPECT_EOF - 发送命令的字符串需要用双引号包裹,单引号在Tcl/expect中不会处理转义,比如原来的
内容的提问来源于stack exchange,提问作者00sax
相关产品推荐
相关产品推荐

