如何在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会话的脚本接收命令,另一个脚本仅负责发送命令到管道。
- 先创建命名管道:
mkfifo /tmp/ssh_cmd_pipe
- 维持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
- 发送命令的脚本(
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
相关产品推荐
相关产品推荐

