嵌入式期望脚本 return 成功或失败 bash

Embedded expect script return success or failure to bash

我在 bash 脚本中嵌入了一个 expect 脚本,以便在服务器上放置一些 ftp 文件。 我试图让 expect 脚本 return 向 bash 过程输出成功或失败的值,以便我可以对其采取行动,但我失败得很惨。 脚本如下所示:

sftper()
{
    local filename=""
    local user="user"
    local password="password"
    local host="ftpserver"
    local in=1

    IFS= read -r -d '' expect_commands <<EOD >echo_log
    spawn /usr/bin/sftp $user@$host
    expect "*assword:"
    send "$password\r"
    expect "sftp>"
    send "put $filename\r"
    expect "sftp>"
    send "bye\r"
    interact
EOD
    expect -c "${expect_commands//
    /;}"
    in="$?"
    return "$in"
}

`

拜托,我做错了什么,我该如何解决这个问题?

脚本中最令人惊讶的是您调用了 interact,这是一个 Expect 命令,允许用户直接连接到生成的程序。但是,在您向 sftp 发送 bye 命令后,它不太可能有用; close; exit 序列更符合预期(关闭控制虚拟终端并退出 Expect 脚本)。

因此,我期望更像:

    IFS= read -r -d '' expect_commands <<EOD >echo_log
    spawn /usr/bin/sftp $user@$host
    expect "*assword:"
    send "$password\r"
    expect "sftp>"
    send "put $filename\r"
    expect "sftp>"
    send "bye\r"
    close
    exit
EOD

更一般地说,您应该考虑如果出现问题会发生什么(例如,如果密码已过期),并且编写完整的 expect 脚本可能比 运行 gauntlet 更容易所有 shell 引用。我添加了一些小的解释性评论…

# Assuming this is in a file called do_sftp_put.exp

# set up the variables; first line is how to get a command line argument
set filename [lindex $argv 0]
set user "user"
set password "password"
set host "ftpserver"

# launch sftp
spawn /usr/bin/sftp $user@$host

# login sequence
expect "*assword:"
send "$password\r"
expect "sftp>"

# send the file
send "put $filename\r"
expect "sftp>"

# Shut down
send "bye\r"
close
exit

那么您的 shell 脚本包装器将变得更加简单:

sftper()
{
    expect /path/to/do_sftp_put.exp ""
    return $?
}

这里不需要太多花哨的引用;这就是一切设计工作的方式。是的,expect 脚本需要仔细保护,只有您可以阅读它,因为它包含实时凭据,但您之前在 shell 代码中遇到过这个问题,所以这不是什么新鲜事。


[编辑]:当然,这仍然没有检测到错误。为此,您需要一个更复杂的脚本。最常见的错误之一是密码错误(或与用户不匹配)。为了解决这个问题,我们采取这一点:

# login sequence
expect "*assword:"
send "$password\r"
expect "sftp>"

并将其升级为:

# login sequence
expect "*assword:"
send "$password\r"
expect {
    "password" {
        # It's asking for the password *again*; must be wrong!
        # Kill the sftp program and exit with an error code
        close
        exit 1
    }
    "sftp>"
}

弄清楚如何检测错误并不总是微不足道的(您可以使用正则表达式等,并且您需要准确了解您正在处理的问题),在某些情况下,备份实际上更好并完全尝试另一种方法。具体对于 sftp,I 将尝试配置 SSH 身份,以便我可以使用基于密钥的身份验证而不是基于密码的身份验证,因为这在实践中以多种方式更加安全.但是,这对您的操作来说是一个重大变化,因此绝对不在问题范围内。