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

如何在Expect脚本间正确共享SSH连接的spawn id?

解决Expect脚本复用SSH会话spawn id的问题

直接传递spawn id字符串(比如exp5)到另一个Expect脚本是行不通的——每个Expect进程都维护着自己独立的通道上下文,跨进程的spawn id标识符没有任何意义,这就是你遇到“can not find channel named 'exp5'”错误的原因。

下面是两种可行的解决方案:

方案1:单Expect脚本维持会话,通过标准输入接收命令

让一个长期运行的Expect脚本建立并维持SSH连接,Node.js通过该脚本的标准输入发送命令,脚本将命令转发到SSH会话,再把执行结果通过标准输出返回给Node.js。

示例脚本(ssh_session.exp):

#!/usr/bin/expect
# 替换为你的SSH连接信息
set user "your_username"
set device "device_ip_or_hostname"
set password "your_password"
set prompt "$ " ;# 替换为设备的命令行提示符

# 建立SSH连接
spawn ssh ${user}@${device}
expect {
    "password:" {
        send "${password}\r"
        exp_continue
    }
    $prompt {}
}

# 循环处理外部输入的命令
while {[gets stdin cmd] != -1} {
    # 处理退出命令
    if {$cmd eq "exit"} {
        send "exit\r"
        expect eof
        exit 0
    }
    # 发送命令到设备
    send "${cmd}\r"
    # 等待命令执行完成,匹配提示符
    expect $prompt
    # 输出执行结果(可根据需求调整输出内容)
    puts $expect_out(buffer)
}

Node.js侧可以通过child_process模块和该脚本交互:

const { spawn } = require('child_process');

const sshSession = spawn('./ssh_session.exp');

// 发送命令到Expect脚本
sshSession.stdin.write('show version\r');
sshSession.stdin.write('show interfaces\r');
// 退出会话
sshSession.stdin.write('exit\r');

// 接收脚本返回的结果
sshSession.stdout.on('data', (data) => {
    console.log(`设备输出: ${data}`);
});

sshSession.stderr.on('data', (data) => {
    console.error(`错误信息: ${data}`);
});

方案2:通过命名管道(FIFO)实现跨脚本通信

如果一定要拆分脚本,可以用命名管道让维持SSH会话的脚本接收命令,另一个脚本仅负责发送命令到管道。

  1. 先创建命名管道:
mkfifo /tmp/ssh_cmd_pipe
  1. 维持SSH会话的脚本(ssh_listen.exp):
#!/usr/bin/expect
set user "your_username"
set device "device_ip_or_hostname"
set password "your_password"
set prompt "$ "
set pipe_path "/tmp/ssh_cmd_pipe"

# 建立SSH连接
spawn ssh ${user}@${device}
expect {
    "password:" {
        send "${password}\r"
        exp_continue
    }
    $prompt {}
}

# 打开命名管道并监听输入
set pipe [open $pipe_path r]
fileevent $pipe readable {
    if {[gets $pipe cmd] != -1} {
        if {$cmd eq "exit"} {
            send "exit\r"
            expect eof
            close $pipe
            file delete $pipe_path
            exit 0
        }
        send "${cmd}\r"
        expect $prompt
        puts $expect_out(buffer)
    }
}

# 保持脚本运行
vwait forever
  1. 发送命令的脚本(send_cmd.exp):
#!/usr/bin/expect
set cmd [lindex $argv 0]
set pipe_path "/tmp/ssh_cmd_pipe"

if {![file exists $pipe_path]} {
    puts "错误:会话管道不存在,请先启动ssh_listen.exp"
    exit 1
}

set pipe [open $pipe_path w]
puts $pipe $cmd
close $pipe

使用时先启动ssh_listen.exp,再通过send_cmd.exp发送命令:

./ssh_listen.exp &
./send_cmd.exp "show version"
./send_cmd.exp "exit"

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 00:26:14