Node.js如何改写ssh2事件监听代码为async/await获取Promise结果
问题根源
外层作用域无法获取data的核心原因是:ssh2的连接建立、命令执行、流数据接收全是异步非阻塞逻辑,JS主线程不会阻塞等待这类异步操作执行完成,在外层编写console.log(data)逻辑时,数据流事件尚未触发,对应变量还未赋值,自然无法打印出内容。
将整个SSH执行流程封装进Promise、配合async/await语法,就可以用近似同步的写法获取异步返回的命令执行结果。
可直接使用的改写代码
const { readFileSync } = require('fs'); const { Client } = require('ssh2'); /** * 远程SSH执行命令封装 * @param {object} sshConfig SSH连接配置 * @param {string} command 要执行的shell命令 * @returns {Promise<string>} 命令执行的STDOUT输出 */ function execSSHCommand(sshConfig, command) { return new Promise((resolve, reject) => { const conn = new Client(); let stdout = ''; let stderr = ''; conn.on('ready', () => { conn.exec(command, (err, stream) => { if (err) { conn.end(); return reject(err); } stream.on('close', (code, signal) => { conn.end(); if (code !== 0) { return reject(new Error(`命令执行退出码异常: ${code}, 信号: ${signal}, 错误输出: ${stderr}`)); } // 正常退出返回标准输出 resolve(stdout.trim()); }).on('data', (data) => { // 拼接收集所有stdout数据块 stdout += data.toString('utf8'); }).stderr.on('data', (data) => { // 收集stderr输出 stderr += data.toString('utf8'); }); }); }).on('error', (err) => { // 捕获连接阶段错误 reject(new Error(`SSH连接失败: ${err.message}`)); }).connect(sshConfig); }); } // 调用示例 async function runRemoteTask() { const sshConfig = { host: 'x.x.x.x', port: 22, username: 'abc', privateKey: readFileSync('./id_rsa') }; const targetCommand = 'python ~/test.py'; try { const output = await execSSHCommand(sshConfig, targetCommand); // 这里就能拿到完整的stdout输出,可在外层任意逻辑中使用 console.log('远程脚本执行结果:', output); return output; } catch (err) { console.error('执行出错:', err.message); throw err; } } // 启动执行 runRemoteTask();
注意事项
- 所有Promise返回的异步结果,必须在
await关键字之后才能拿到,不要在await语句之前提前访问结果变量 - 上述代码默认把输出转成utf8字符串,如果命令返回二进制内容(比如文件流),去掉
toString('utf8')直接拼接Buffer即可 - 连接失败、命令执行报错、脚本非0退出的异常都会被Promise抛出,必须用
try/catch块捕获处理 - 代码已经在流关闭、报错的所有分支主动调用了
conn.end()断开连接,不会出现连接泄漏问题
内容的提问来源于stack exchange,提问作者Rajarshi Das
相关产品推荐
相关产品推荐

