首页 > 解决方案 > 从标准输入读取程序输出而不是使用 spawn

问题描述

我正在尝试编写一个生成 ssh 进程并使用密码进行身份验证的 shell 函数。然后,我想使用生成的进程来做更多的事情。

这是我到目前为止的功能:

ssh_cmd() {
    ssh $USER@$HOST 2>&1 | expect -c "
        log_user 0
        set timeout 5
        expect password: {
            send \"$PASS\n\"
            sleep 2
            log_user 1
        }
    "
}

然后我想在其他地方使用给定的函数来与 ssh 进程进行交互,如下所示:

ssh_cmd | expect -c "
    expect '#' {
        send pwd\n
        send exit\n
    }
    expect eof
"

但是,使用 expect 选项运行该ssh_cmd函数-d会得到以下结果:

expect version 5.45.4

expect: does "" (spawn_id exp0) match glob pattern "password:"? no
ubnt@ui1's password: expect: timed out

据我了解,输出ssh没有正确传输。我知道执行此操作的常用方法是使用spawn,但这意味着进程将在期望退出后被终止,并且我无法拥有一个通用函数来验证 ssh 会话并保持进程处于活动状态以供进一步使用。

标签: shellexpect

解决方案


你设计的东西是行不通的。Expect 需要使用命令从expect解释器中生成进程。spawn将命令的标准输出传递给期望是不够的。

你可以试试这个:

ssh_cmd() {
    # a default bit of code if user does not provide one.
    # you probably want some checking and emit an error message.
    local user_code=${1:-set timeout 1; send "exit\r"; expect eof}
    expect -c "
        log_user 0
        set timeout 5
        spawn ssh -l $USER $HOST
        expect password: {
            send \"$PASS\n\"
            sleep 2
            log_user 1
        }
        $user_code
    "
}

然后像这样调用它:

ssh_cmd '
    expect "#" {
        send pwd\r
        send exit\r
    }
    expect eof
'

请注意,单引号在期望中没有特殊含义,它们只是普通字符:您可能不希望提示是 3 字符模式'#'


推荐阅读