将Expect脚本嵌入Bash脚本中

8

我有以下脚本:

#!/bin/bash

if [ `hostname` = 'EXAMPLE' ]
then

/usr/bin/expect << EOD

spawn scp -rp host:~/outfiles/ /home/USERNAME/outfiles/
expect "id_rsa':"
send "PASSWORD\r"
interact

spawn scp -rp host:~/errfiles/ /home/USERNAME/errfiles/
expect "id_rsa':"
send "PASSWORD\r"
interact

expect eof

EOD

echo 'Successful download'
fi

很不幸,它似乎无法工作,我收到了一个错误信息:
spawn scp -rp host:~/outfiles/ /home/USERNAME/outfiles/
Enter passphrase for key '/home/USERNAME/.ssh/id_rsa': interact: spawn id exp0 not open
    while executing
"interact"

我不知道它的含义和为什么它不起作用。然而,当我使用非嵌入式的Expect脚本编写上述代码时:
#!/usr/bin/expect

spawn scp -rp host:~/outfiles/ /home/USERNAME/outfiles/
expect "id_rsa':"
send "PASSWORD\r"
interact

spawn scp -rp host:~/errfiles/ /home/USERNAME/errfiles/
expect "id_rsa':"
send "PASSWORD\r"
interact

它可以正常工作,没有任何问题。那么我做错了什么?

注意:当有人发布使用Expect使用scpssh的问题时,给出的答案是使用RSA密钥。我尝试过,不幸的是,在我的一台计算机上,GNOME钥匙环中存在一些糟糕的错误,这意味着我无法从RSA密钥中删除我的密码,这正是我尝试编写上述带有if语句的脚本的原因。所以请不要告诉我使用RSA密钥。


1
如果你希望执行定界符之间的文本(在解释之前不进行变量或特殊字符扩展)完全相同,那么你必须引用你的 here-doc 定界符,像这样:/usr/bin/expect << "EOD" ... EOD这个问题类似,它有相同的问题,但是方向相反。 - Aserre
我刚刚尝试了/usr/bin/expect << "EOD",但它仍然显示错误spawn scp -rp host:~/outfiles/ /home/USERNAME/outfiles/ Enter passphrase for key '/home/USERNAME/.ssh/id_rsa': interact: spawn id exp0 not open while executing "interact" Successful download - Marses
1个回答

12

你的Bash脚本正在将Expect命令作为标准输入传递给expect。这就是这个here-document <<EOD的作用。然而,根据手册expect期望其命令以文件的形式或作为-c参数的一部分提供。以下有三个选择。请注意:这些都没有经过测试。

  1. 进程替换和here-document:

    expect <(cat <<'EOD'
    spawn ... (your script here)
    EOD
    )
    
    EOD结束了here-document的输入,然后整个内容被包裹在<( )进程替换块中。结果是,expect会看到一个临时文件名,其中包含您here-document的内容。
    正如@Aserre所指出的那样,<<'EOD'中的引号意味着您here-document中的每个内容都将被视为字面量。如果您想展开Bash变量等脚本内部的内容,请去掉这些引号。
    编辑变量+here-document:
    IFS= read -r -d '' expect_commands <<'EOD'
    spawn ... (your script here)
    interact
    EOD
    
    expect -c "${expect_commands//
    /;}"
    

    是的,在//之后有一个真正的换行符 - 我不确定如何转义它。这将把换行符转换为分号,而man页面说这是必需的。

    感谢这个答案提供了read+heredoc组合方法。

    Shell变量

    expect_commands='
    spawn ... (your script here)
    interact'
    expect -c "${expect_commands//
    /;}"
    

    请注意:期望命令中的任何 '(例如在 id_rsa 之后)都需要替换为 '\'',以离开单引号块、添加一个字面撇号,然后重新进入单引号块。在 // 之后的换行符与前一个选项相同。


谢谢您的帮助,我使用了示例1,并且它起作用了。我很感激。 - Marses
你也可以使用 expect 中的 -f 标志来使用包含命令的文件。但是,该文件可以是 -,类似于 /dev/stdin。这可以是一个文档嵌入式。但是,如果文档嵌入式包含 interact,它将失败,因为 interact 使用 /dev/stdin,但是文档嵌入式已经结束,因此脚本终止。 - kvantour
在 Mac OS X 上工作过。 - trinth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接