在 Bash 脚本中嵌入 Expect 脚本

Embedding an Expect script inside a Bash script

我有以下脚本:

#!/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 密钥中删除我的密码,这正是我尝试使用 [= 编写上述脚本的原因16=] 语句。所以请不要告诉我使用 RSA 密钥。

您的 Bash 脚本正在 expect 的标准输入上传递 Expect 命令。那就是here-document <<EOD does. However, expect... expects its commands to be provided in a file, or as the argument of a -c, per the man page。下面是三个选项。买者自负; none 已测试。

  1. Process substitution 此处文档:

    expect <(cat <<'EOD'
    spawn ... (your script here)
    EOD
    )
    

    EOD 结束 here-document,然后整个内容被包裹在 <( ) 进程替换块中。结果是 expect 将看到一个临时文件名,其中包含您的此处文档的内容。

    正如@Aserre 指出的那样,<<'EOD' 中的引号意味着您此处文档中的所有内容都将按字面意思处理。将它们关闭以在脚本中扩展 Bash 变量等,如果这是你想要的。

  2. 编辑 变量+此处文档:

    IFS= read -r -d '' expect_commands <<'EOD'
    spawn ... (your script here)
    interact
    EOD
    
    expect -c "${expect_commands//
    /;}"
    

    是的,那是 // 之后的一个真正的换行符 - 我不清楚如何转义它。这会将换行符变成分号,man page 说这是必需的。

    感谢 this answerread+heredoc 组合。

  3. Shell变量

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

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