翻译:疯狂的技术宅
原文:http://2ality.com/2018/05/chi...


本文首发微信公众号:jingchengyideng
欢迎关注,每天都给你推送新鲜的前端技术文章


在本中,我们在 Node.js 中把 shell 命令作为子进程运行。然后异步读取这些进程的 stdout 并写入其 stdin。

在子进程中运行 shell 命令

首先从在子进程中运行 shell 命令开始:

const {onExit} = require('@rauschma/stringio');
const {spawn} = require('child_process');async function main() {const filePath = process.argv[2];console.log('INPUT: '+filePath);const childProcess = spawn('cat', [filePath],{stdio: [process.stdin, process.stdout, process.stderr]}); // (A)await onExit(childProcess); // (B)console.log('### DONE');
}
main();

解释:

  • 我们用了 spawn(),它可以使我们在命令运行时访问命令的 stdin,stdout 和 stderr。

    • 在 A 行中,我们将子进程的 stdin 连接到当前进程的 stdin。
    • B 行等待该过程完成。

等待子进程通过 Promise 退出

函数 onExit()如下所示。

function onExit(childProcess: ChildProcess): Promise<void> {return new Promise((resolve, reject) => {childProcess.once('exit', (code: number, signal: string) => {if (code === 0) {resolve(undefined);} else {reject(new Error('Exit with error code: '+code));}});childProcess.once('error', (err: Error) => {reject(err);});});
}

子进程的实现

以下代码用 @rauschma/stringio 异步写入以 shell 命令运行的子进程的 stdin

const {streamWrite, streamEnd, onExit} = require('@rauschma/stringio');
const {spawn} = require('child_process');async function main() {const sink = spawn('cat', [],{stdio: ['pipe', process.stdout, process.stderr]}); // (A)writeToWritable(sink.stdin); // (B)await onExit(sink);console.log('### DONE');
}
main();async function writeToWritable(writable) {await streamWrite(writable, 'First line\n');await streamWrite(writable, 'Second line\n');await streamEnd(writable);
}

我们为 shell 命令生成一个名为 sink 的独立进程。用 writeToWritable 写入 sink.stdin。它借助 await 异步执行并暂停,以避免缓冲区被消耗太多。
解释:

  • 在A行中,我们告诉 spawn() 通过 sink.stdin'pipe')访问 stdin。 stdout 和 stderr 被转发到 process.stdinprocess.stderr,如前面所述。
  • 在B行中不会 await 写完成。而是 await 子进程 sink 完成。

接下来了解 streamWrite() 的工作原理。

写流操作的 promise

Node.js 写流的操作通常涉及回调(参见文档)。代码如下。

function streamWrite(stream: Writable,chunk: string|Buffer|Uint8Array,encoding='utf8'): Promise<void> {return new Promise((resolve, reject) => {const errListener = (err: Error) => {stream.removeListener('error', errListener);reject(err);};stream.addListener('error', errListener);const callback = () => {stream.removeListener('error', errListener);resolve(undefined);};stream.write(chunk, encoding, callback);});
}

streamEnd()的工作方式是类似的。

从子进程中读取数据

下面的代码使用异步迭代(C行)来读取子进程的 stdout 中的内容:

const {chunksToLinesAsync, chomp} = require('@rauschma/stringio');
const {spawn} = require('child_process');async function main() {const filePath = process.argv[2];console.log('INPUT: '+filePath);const source = spawn('cat', [filePath],{stdio: ['ignore', 'pipe', process.stderr]}); // (A)await echoReadable(source.stdout); // (B)console.log('### DONE');
}
main();async function echoReadable(readable) {for await (const line of chunksToLinesAsync(readable)) { // (C)console.log('LINE: '+chomp(line))}
}

解释:

  • A行:我们忽略 stdin,希望通过流访问 stdout 并将 stderr 转发到process.stderr
  • B行:开始 awat 直到 echoReadable() 完成。没有这个 awaitDONE 将会在调用 source.stdout 之前被输出。

在子进程之间进行管道连接

在下面的例子中,函数transform() 将会:

  • source 子进程的 stdout 中读取内容。

    • 将内容写入 sink 子进程的 stdin

换句话说,我们正在实现类似 Unix 管道的功能:

cat someFile.txt | transform() | cat

这是代码:

const {chunksToLinesAsync, streamWrite, streamEnd, onExit}= require('@rauschma/stringio');
const {spawn} = require('child_process');async function main() {const filePath = process.argv[2];console.log('INPUT: '+filePath);const source = spawn('cat', [filePath],{stdio: ['ignore', 'pipe', process.stderr]});const sink = spawn('cat', [],{stdio: ['pipe', process.stdout, process.stderr]});transform(source.stdout, sink.stdin);await onExit(sink);console.log('### DONE');
}
main();async function transform(readable, writable) {for await (const line of chunksToLinesAsync(readable)) {await streamWrite(writable, '@ '+line);}await streamEnd(writable);
}

扩展阅读

  • 博客:“通过 Node.js 的异步迭代读取流”
  • “探索ES2018和ES2019”中的“异步迭代 一章
  • “探索ES2016和ES2017”中的“异步功能” 一章

欢迎继续阅读本专栏其它高赞文章:

  • 12个令人惊叹的CSS实验项目
  • 世界顶级公司的前端面试都问些什么
  • CSS Flexbox 可视化手册
  • 过节很无聊?还是用 JavaScript 写一个脑力小游戏吧!
  • 从设计者的角度看 React
  • CSS粘性定位是怎样工作的
  • 一步步教你用HTML5 SVG实现动画效果
  • 程序员30岁前月薪达不到30K,该何去何从
  • 第三方CSS安全吗?
  • 谈谈super(props) 的重要性

本文首发微信公众号:jingchengyideng

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章


在 Node.js 中用子进程操作标准输入/输出相关推荐

  1. Node.js SQL数据库操作 (上)(操作MySQL数据库及 数据库连接池)

    文章目录 Node.js MySQL驱动 操作 MySQL 数据库 连接 MySQL 数据库 增删改查操作 防止 SQL 注入攻击 数据库连接池操作 Node.js MySQL驱动 Node.js的原 ...

  2. linux 页面内容输出,Node.js 一个简单的页面输出

    最近决定重拾node.js,用它来做一个合并JS文件的东西.由于忘得差不多了,先看能不能输出一个页面来再说.以下是我的一些笔记,省得以后又忘净光-- 安装过程就不说了.如果成功是能使用node的命令. ...

  3. 子进程会继承父进程的哪些内容_【学习教程】Node.js创建子进程方法

    来源 | https://github.com/CommanderXL/biu-blog/issues/25 exec 其中exec可用于在指定的shell当中执行命令.不同参数间使用空格隔开,可用于 ...

  4. Node.js 模块化的操作,简单明了的代码帮助你明白后端的实现和前端之前的交互,及解决跨域等问题

      Node.JS 模块化 1.require 导入模块const http=require('http')2.exports 导出模块db={name:'jine',age:22} module.e ...

  5. node.js连接MySQL操作及注意事项

    node.js作为服务端的js运行环境已经出现了有几年了,最近我有个朋友也在做这方面的开发,但是也是刚刚接触,遇到了很多坑.前几天他们在操作数据库的时候出现了点问题,后来我们一起看了看,其实都是nod ...

  6. Node.js使用mongoose操作mongodb

    软件配置: 1.node v8.9.3 2. npm 5.5.1 3. mongoose及MongoDB版本见下package.json // package.json {   "name& ...

  7. mysql.createPool(db),Node.js中JavaScript操作MySQL的常用方法整理

    一.建立数据库连接:createConnection(Object)方法      该方法接受一个对象作为参数,该对象有四个常用的属性host,user,password,database.与php中 ...

  8. node.js使用bull操作任务队列

    https://github.com/OptimalBits/bull 1.简介 bull是最快,最可靠的基于Redis的Node队列. 1.1 特性 bull提供了以下特性: 由于采用无轮询设计,因 ...

  9. [Node.js] 使用nodejs操作mysql数据库

    使用nodejs操作mysql数据库 安装命令 npm init -y 安装命令 npm i mysql --registry=https://registry.npm.taobao.org cons ...

最新文章

  1. 不会画饼的领导,不值得托付
  2. 明星软件工程师的十种特质
  3. Boost:等待和通知操作的模糊测试
  4. Verilog定义计算位宽的函数clogb2
  5. HTML中transform菜鸟,HTML canvas
  6. 安卓动态调试七种武器之离别钩 – Hooking(下)
  7. 【clickhouse】clickhouse的系统表
  8. 「分布式系统理论」系列专题整理
  9. 实现一个闹钟_iPhone 闹钟停止时,如何自动播放你喜欢的音乐
  10. 【Matlab学习笔记】【图像滤波去噪】以-4,-8为中心的拉普拉斯滤波器
  11. DHCP服务在企业网络中的应用
  12. VMware Esxi7.0.3f集成最新网卡-v2(2022年7月版)
  13. 学生HTML个人网页作业作品~蛋糕甜品店铺共11个页面(HTML+CSS+JavaScript)
  14. win10子系统ubuntu文件夹位置_Win10 Linux子系统任何直接访问文件
  15. linux解压时的tar -zxvf是什么意思?
  16. 哔哩哔哩第三方神器软件,早该用上了
  17. adb root 权限运行
  18. PS第一课--基本操作
  19. 解决 Client.Timeout exceeded while awaiting headers或Docker Pulling fs layer net/http: TLS handshake的方案
  20. 企业发放的奖金根据利润I提成

热门文章

  1. 在Eclipse中使用Maven构建Spring项目
  2. 跨平台PHP调试器设计及使用方法——拾遗
  3. DllMain中不当操作导致死锁问题的分析——DllMain中要谨慎写代码(完结篇)
  4. Ubuntu上Vim安装NERDTree插件操作步骤
  5. AES(Advanced Encryption Standard) Intrinsics各函数介绍
  6. 图像空间变换--imtransform
  7. pythonapi是什么_python接口自动化(一)--什么是接口、接口优势、类型(详解)...
  8. oracle rman实时备份吗,ORACLE-RMAN自动备份和恢复
  9. fedora mysql默认密码忘记_Linux fedora 24 忘记密码图形化界面修改root密码的方法
  10. Java项目:日历万年历(java+swing)