在 bash 脚本中嵌入期望

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41165719/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 15:32:23  来源:igfitidea点击:

Embedding an expect inside a bash script

bashexpect

提问by Marses

I realise similar questions have been asked before, I looked at them and tried to apply what I learned and have the following 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

Unfortunately it doesn't seem to work and I get an error message:

不幸的是,它似乎不起作用,我收到一条错误消息:

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"

I don't know what it means and why it doesn't work. However, when I wrote the above code using a not-embedded expect script:

我不知道这意味着什么以及为什么它不起作用。但是,当我使用未嵌入的 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

It worked without any problems. So what am I doing wrong?

它没有任何问题。那我做错了什么?

NOTE: Often when someone posts a question about using expectto use scpor sshthe answer given is to use rsa keys. I tried, unfortunately on one of my computers there is some crappy bug with the gnome keyring that means that I can't remove my password from the rsa-key, which is exactly why I'm trying to write the above script with an if statement. So please don't tell me to use rsa keys.

注:通常当有人发布一个有关使用问题,expect使用scpssh给出的答案是使用RSA密钥。我试过了,不幸的是,在我的一台计算机上,gnome 密钥环存在一些糟糕的错误,这意味着我无法从 rsa-key 中删除我的密码,这正是我尝试使用 if 编写上述脚本的原因陈述。所以请不要告诉我使用 rsa 密钥。

回答by cxw

Your bash script is passing the expectcommands on the standard input of expect. That is what the here-document<<EODdoes. However, expect... expects its commands to be provided in a file, or as the argument of a -c, per the man page. Three options are below. Caveat emptor; none have been tested.

您的 bash 脚本正在 .bashrcexpect的标准输入上传递命令expect。这就是here-document<<EOD所做的。但是,expect... 期望它的命令在文件中提供,或者作为 a 的参数-c,根据手册页。下面是三个选项。买者自负; 没有经过测试。

  1. Process substitutionwith here-document:

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

    The EODends the here-document, and then the whole thing is wrapped in a <( )process substitution block. The result is that expectwill see a temporary filename including the contents of your here-document.

    As @Aserre noted, the quotes in <<'EOD'mean that everything in your here-document will be treated literally. Leave them off to expand bash variables and the like inside the script, if that's what you want.

  2. EditVariable+here-document:

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

    Yes, that is a real newline after //- it's not obvious to me how to escape it. That turns newlines into semicolons, which the man pagesays is required.

    Thanks to this answerfor the read+heredoc combo.

  3. Shell variable

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

    Note that any 'in the expect commands (e.g., after id_rsa) will need to be replaced with '\''to leave the single-quote block, add a literal apostrophe, and then re-enter the single-quote block. The newline after //is the same as in the previous option.

  1. 使用此处文档进行过程替换

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

    EOD结束这里的文档,然后整个事情被包裹在一个<( )进程替换块。结果是expect将看到一个临时文件名,包括您的此处文档的内容。

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

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

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

    是的,这是一个真正的换行符//- 我不知道如何逃避它。这会将换行符转换为分号,手册页说这是必需的。

    感谢这个答案read+定界符组合。

  3. 外壳变量

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

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