You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Shell脚本中复用Expect生成的SSH会话的可行性咨询

如何在Shell脚本中复用Expect创建的SSH会话?

我想在Shell脚本里执行多段Expect代码,且各段Expect之间能运行Shell命令。以下是一个概念性的非工作示例:第一段Expect用于SSH连接远程节点,后续代码段希望在同一个远程会话中执行其他命令:

#!/bin/bash

# 第一段Expect代码
/usr/bin/expect - <<EOF
    global spawn_id
    spawn /bin/bash
    expect -re ".*\$"
    spawn -- "ssh   -oStrictHostKeyChecking=no -oCheckHostIP=no admin@localstack1\r"
    expect -re  ".*assword.*"
    send 'admin\r'
    expect -re ".*\$"
    interact
EOF

# 运行Shell命令
ls -lrt


# 第二段Expect代码
/usr/bin/expect - <<EOF
    expect -re ".*\$"
    send -- "hostname\r"
    expect -re ".*\$"
EOF

当前方案无法实现,想请教:为什么不像pexpect那样,找不到复用已生成Shell会话(传递子对象)的方法?另外,命令列表、预期结果和超时时间均由外部程序提供,无法硬编码到Expect脚本中,有时仅需一组命令,有时则需要多组。


为什么无法跨Expect进程复用会话?

每一次/usr/bin/expect调用都是独立的操作系统进程,进程之间的资源(包括spawn_id对应的会话句柄)是完全隔离的。第一段Expect执行结束后,它创建的SSH会话要么因interact转交给用户,要么随Expect进程退出而被关闭,第二段Expect进程根本无法访问前一段的会话资源。

可行解决方案

1. 单Expect进程+外部参数传递(推荐)

将所有Expect逻辑整合到单个Expect进程中,通过环境变量或命令行参数把外部提供的命令、预期结果、超时时间传入,让Expect在同一个SSH会话内依次执行所有操作。示例如下:

#!/bin/bash

# 模拟从外部程序获取命令、预期结果、超时时间(实际中替换为外部数据来源)
COMMANDS=("hostname" "df -h")
EXPECT_PATTERNS=(".*\$" ".*\$")
TIMEOUTS=(10 15)

# 将数组转为Expect可识别的字符串(用|分隔)
CMD_LIST=$(IFS='|'; echo "${COMMANDS[*]}")
EXP_LIST=$(IFS='|'; echo "${EXPECT_PATTERNS[*]}")
TOUT_LIST=$(IFS='|'; echo "${TIMEOUTS[*]}")

# 调用单个Expect进程执行所有操作
/usr/bin/expect -c "
    # 拆分传入的参数为数组
    set commands [split \"$CMD_LIST\" \"|\"]
    set expect_patterns [split \"$EXP_LIST\" \"|\"]
    set timeouts [split \"$TOUT_LIST\" \"|\"]

    # 初始化SSH连接
    spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no admin@localstack1
    expect -re \".*assword.*\"
    send \"admin\r\"
    expect -re \".*\$\"

    # 循环执行所有命令
    for {set i 0} {\$i < [llength \$commands]} {incr i} {
        set cmd [lindex \$commands \$i]
        set pattern [lindex \$expect_patterns \$i]
        set timeout_val [lindex \$timeouts \$i]

        set timeout \$timeout_val
        send \"\$cmd\r\"
        expect -re \$pattern
    }

    # 可选:退出远程会话或进入交互模式
    # send \"exit\r\"
    # expect eof
    # interact
"

# 中间执行Shell命令
ls -lrt

2. 简化场景:改用sshpass批量执行

如果仅需执行简单远程命令,无复杂交互需求,可以用sshpass直接批量执行,虽然每次会重建SSH连接,但实现简单:

#!/bin/bash
# 第一组远程命令
sshpass -p "admin" ssh -oStrictHostKeyChecking=no -oCheckHostIP=no admin@localstack1 <<REMOTE_CMDS
hostname
df -h
REMOTE_CMDS

# 中间执行Shell命令
ls -lrt

# 第二组远程命令
sshpass -p "admin" ssh -oStrictHostKeyChecking=no -oCheckHostIP=no admin@localstack1 <<REMOTE_CMDS
uptime
free -m
REMOTE_CMDS

3. 复杂交互场景:用文件/管道传递会话控制(不推荐)

如果必须在Expect执行中途切换到Shell,可以通过命名管道或临时文件传递会话状态,但实现复杂且可靠性低,仅适合特殊场景,一般不建议使用。


内容的提问来源于stack exchange,提问作者monk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 01:55:35