【问题标题】:How do I tell expect that I have finished the interactive mode?我如何告诉期望我已经完成了交互模式?
【发布时间】:2020-05-26 21:31:48
【问题描述】:

我正在用 bash 编写一些期望命令。

脚本:

#!/bin/bash  
set timeout -1  

expect -c "  

spawn telnet $IP $PORT1  
sleep 1  
send \"\r\"  
send \"\r\"  
expect Prompt1>  
interact timeout 20 {  
sleep 1  
}  

expect {  
Prompt2> {send \"dir\r\" }  
}    

"  

我对脚本的意图是,首先让它telnet到一台机器上,当它看到Prompt1时,让它把控制权交给我,我会执行一个命令来加载一个特定的图像。然后等到 Prompt2 出现(表示图像已加载)。然后让它执行进一步的命令集。

运行脚本后,我可以进入交互模式,加载我的图像。问题是退出远程机器上的交互模式并将控制权交还给它。

我得到的错误:

expect: spawn id exp4 not open  
    while executing  
"expect -nobrace Prompt2 {send "dir\r" }"  
    invoked from within  
"expect {    
Prompt2 {send "dir\r" }  
}"  

我该怎么做?

【问题讨论】:

  • 您打算如何退出交互模式?这是期待prompt2吗?
  • 超时值 20,我想我会出局的。我在这里错了吗?一旦我出去,我希望 Expect 收回控制权并期待 Prompt2。然后它可以执行另一组命令。

标签: expect


【解决方案1】:

你的问题有两个...

  1. 您应该与明确的return 进行交互,并给它一些方法来知道您已释放控制...在这种情况下,我使用三个加号并按 Enter。

    李>
  2. 在您返回控制权后,脚本将需要再次获取提示,这意味着您在返回控制权后要做的第一件事是发送另一个\r。我编辑了我认为你正在尝试做的事情......

示例如下...

#!/bin/bash  
set timeout -1  

expect -c "  

spawn telnet $IP $PORT1  
sleep 1  
send \"\r\"  
send \"\r\"  
expect Prompt1>  
interact +++ return

send \"\r\"
expect {  
Prompt2> {send \"dir\r\" }  
}
"

【讨论】:

    【解决方案2】:

    返回 = 失败

    return 对我不起作用,因为在我的情况下,它不是一个可以简单地再次提示我相同问题的 shell。在return 之前,我无法弄清楚如何让它与打印的内容相匹配。

    expect_out(修复上述解决方案)=失败

    手册说:

    Upon matching a pattern (or eof or full_buffer), any matching and previously unmatched output is saved in the variable expect_out(buffer).
    

    但我无法让它工作(除了我在下面使用它的地方,结合-indices 使它在那里工作,并且不知道如何让它工作以将以前的输出馈送到新的expect { ... }块。)

    expect_user

    使用expect_user 的解决方案here 对我也不起作用,因为它没有解释,也没有按照我的意愿使用,所以不知道如何在我的实际期望文件中应用这个有限的示例.

    我的解决方案

    所以我所做的就是避免使用交互模式,而只是有一种方法来提供输入,一次一行。它甚至适用于箭头键和alt+...,(在 dpkg 对话问题中)但有时不适用于简单的 <enter>(对于 dpkg 对话中的那些,按 alt+y 表示 <Yes> 或 alt+o 表示 <Ok>)。 (有人知道如何发送回车吗?不是'\n',而是像dpkg Dialog这样的回车键?)

    -i $user_spawn_id 部分意味着它不仅查看生成的进程,还查看用户键入的内容。这会影响它之后的所有内容,因此您使用expect_after 或将其放在其余部分下方,而不是expect_before-indices 可以读取匹配的正则表达式的捕获部分。 expect_out(1,string) 是我想要的部分(除了冒号)。

    expect_after {
        -i $user_spawn_id
        # single line custom input; prefix with : and the rest is sent to the application
        -indices -re ":(.*)" {
            send "$expect_out(1,string)"
        }
    }
    

    使用expect_after 意味着它将应用于所有后续expect 块,直到下一个expect_after。因此,您可以将其放在文件中通常的 expect 行之上的任何位置。

    和我的案例/目的

    我想自动化 do-release-upgrade,它不能正确支持通常的 Debian 非交互式标志(请参阅here)...它只是挂起并忽略输入,而不是在提出问题后继续。但问题是不可预测的……升级失败意味着您可能会弄乱您的系统,因此需要对交互进行一些回退。

    【讨论】:

      【解决方案3】:

      感谢迈克的建议。 我稍微调整了一下,让它适应了我的问题。

      更改代码:

      expect Prompt1>  
      interact timeout 10 return  
      expect {  
      timeout {exp_continue}  
      Prompt2 {send \"dir\r\" }  
      }  
      

      timeout 10 值与我们最初设置的set timeout -1 无关。因此,我可以在Prompt1 上执行我想要的任何命令,一旦键盘空闲 10 秒,脚本就会重新获得控制权。

      即使在这之后我又遇到了一个问题,在Prompt1 之后,我想执行命令来加载特定的图像。图像加载大约需要 2 分钟。即使使用set timeout -1,脚本也会超时等待Prompt2。我验证过的甚至不是telnet超时。但是解决方案是在expect语句中超时的情况下添加exp_continue。

      为了让你的set timeout -1 生效,它应该放在expect 中的spawn telnet 命令之前。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多