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

如何用Node.js的child_process持久化SSH会话并异步执行多命令?

问题描述

我需要通过Node.js的child_process模块建立与Linux机器的持久化远程SSH会话,在同一个会话中异步依次执行多个Shell命令。之前用simple-ssh包可以实现,但现在要求不能依赖第三方工具。

原simple-ssh实现示例

var SSH = require("simple-ssh")

var ssh = new SSH({
    host: 'remote_host',
    user: 'username',
    pass: 'password'
});

ssh.exec('pwd', {
        out: console.log.bind(console)
    })
    .exec('ls', {
        out: console.log.bind(console)
    })
    .exec('date', {
        out: console.log.bind(console)
    })
    .start();

我的尝试及问题

尝试1:多次创建SSH会话

每次执行命令都新建一个SSH进程,虽然能分开输出,但会建立多个连接,不符合持久化会话的要求:

const { spawn } = require('child_process');
const child1 = spawn('sshpass', ['-p', 'password', 'ssh', '-T', '-o', 'StrictHostKeyChecking=no', `'username'@'remote_host'`]);
       
child1.stdin.write('password\n');

child1.stdout.on('data', (data) => {
    console.log(`stdout: ${data}`);
});
child1.stderr.on('data', (data) => {
    console.error(`stderr: ${data}`);
});
child1.on('close', (code) => {
    console.log(`child process exited with code ${code}`);
});


const child2 = spawn('sshpass', ['-p', 'password', 'ssh', '-T', '-o', 'StrictHostKeyChecking=no', `'username'@'remote_host'`]);
       
child2.stdin.write('password\n');

child2.stdout.on('data', (data) => {
    console.log(`stdout: ${data}`);
});
child2.stderr.on('data', (data) => {
    console.error(`stderr: ${data}`);
});
child2.on('close', (code) => {
    console.log(`child process exited with code ${code}`);
});
  
const child3 = spawn('sshpass', ['-p', 'password', 'ssh', '-T', '-o', 'StrictHostKeyChecking=no', `'username'@'remote_host'`]);
       
child3.stdin.write('password\n');

child3.stdout.on('data', (data) => {
    console.log(`stdout: ${data}`);
});
child3.stderr.on('data', (data) => {
    console.error(`stderr: ${data}`);
});
child3.on('close', (code) => {
    console.log(`child process exited with code ${code}`);
});

尝试2:一次性发送所有命令

在同一个会话里连续发送命令,但所有命令的输出会混在一起,无法区分每个命令的结果:

const { spawn } = require('child_process');
const child = spawn('sshpass', ['-p', 'password', 'ssh', '-T', '-o', 'StrictHostKeyChecking=no', `'username'@'remote_host'`]);
       
child.stdin.write('pwd\n');
child.stdin.write('ls -al\n');
child.stdin.write('date\n');
        
child.stdout.on('data', (data) => {
    console.log(`stdout: ${data}`);
});

child.stderr.on('data', (data) => {
    console.error(`stderr: ${data}`);
});

child.on('close', (code) => {
    console.log(`child process exited with code ${code}`);
});

核心问题:怎么用child_process.spawn在同一个持久化SSH会话中,异步依次执行多个命令,并且能分别获取每个命令的输出?


解决方案

要实现这个需求,关键是监听前一个命令的执行完成信号,再发送下一个命令,同时给每个命令的输出做标识,方便区分结果。

实现思路

  1. 启动单个SSH会话进程,保持连接持久化;
  2. 维护待执行命令的队列;
  3. 监听SSH进程输出,通过远程Shell的提示符(如$或#)判断前一个命令是否执行完成;
  4. 完成一个命令后,从队列取出下一个命令发送;
  5. 单独收集并打印每个命令的输出,避免混淆。

完整代码示例

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

// SSH配置信息
const sshConfig = {
    host: 'remote_host',
    username: 'username',
    password: 'password'
};

// 待执行的命令队列
const commands = ['pwd', 'ls', 'date'];
let currentCmdIndex = 0;
let currentCmdOutput = '';

// 启动持久化SSH会话
const sshProcess = spawn('sshpass', [
    '-p', sshConfig.password,
    'ssh',
    '-T',
    '-o', 'StrictHostKeyChecking=no',
    `${sshConfig.username}@${sshConfig.host}`
]);

// 处理标准输出,判断命令执行状态并收集输出
sshProcess.stdout.on('data', (data) => {
    const output = data.toString();
    
    // 收集当前命令的输出(排除Shell提示符)
    if (!output.includes('$') && !output.includes('#')) {
        currentCmdOutput += output;
    }

    // 检测到提示符,说明当前命令执行完成
    if (output.endsWith('$ ') || output.endsWith('# ')) {
        if (currentCmdIndex < commands.length) {
            // 打印当前命令的结果
            console.log(`=== 命令 "${commands[currentCmdIndex]}" 的输出 ===`);
            console.log(currentCmdOutput.trim());
            console.log('=====================================\n');
            
            // 重置输出缓冲区,准备执行下一个命令
            currentCmdOutput = '';
            currentCmdIndex++;
            
            // 发送下一个命令,所有命令执行完后关闭会话
            if (currentCmdIndex < commands.length) {
                sshProcess.stdin.write(`${commands[currentCmdIndex]}\n`);
            } else {
                sshProcess.stdin.write('exit\n');
            }
        }
    }
});

// 监听错误输出
sshProcess.stderr.on('data', (data) => {
    console.error(`SSH错误输出: ${data.toString()}`);
});

// 会话关闭事件
sshProcess.on('close', (code) => {
    console.log(`SSH会话已关闭,退出码: ${code}`);
});

// 发送第一个命令启动执行流程
sshProcess.stdin.write(`${commands[currentCmdIndex]}\n`);

关键说明

  • 依赖远程Shell的提示符判断命令执行完成,不同系统或自定义提示符需调整判断逻辑;
  • 命令按队列顺序执行,前一个命令完成后才会发送下一个,实现异步依次执行;
  • 每个命令的输出单独收集和打印,避免混在一起;
  • 所有命令执行完毕后自动发送exit关闭SSH会话。

注意事项

  • 确保本地已安装sshpass(密码登录场景),密钥登录可去掉sshpass参数,改用-i指定密钥文件;
  • 若远程Shell有自定义提示符,需修改代码中提示符的判断规则;
  • 可添加超时逻辑,防止单个命令卡住导致会话挂起。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 03:14:51