当 运行 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.