当前位置: 首页 > 编程日记 > 正文

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

翻译:疯狂的技术宅
原文: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

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

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


相关文章:

再见,Python 2.x

整理 | 屠敏来源 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;在技术的长河中&#xff0c;软件、工具、系统等版本的迭代本是常事&#xff0c;但由于使用习惯、版本的兼容性、易用性等因素&#xff0c;很多用户及开发者在使用或做开发的过程中&#xff0c;并不愿意及…

Android UI系列-----CheckBox和RadioButton(1)

主要记录一下CheckBox多选框和RadioGroup、RadioButton单选框的设置以及注册监听器 1.CheckBox 布局文件&#xff1a; <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"android…

C++中struct的使用

C语言继承了C语言的struct&#xff0c;并且加以扩充。在C语言中struct是只能定义数据成员&#xff0c;而不能定义成员函数的。而在C中&#xff0c;struct类似于class&#xff0c;在其中既可以定义数据成员&#xff0c;又可以定义成员函数。结构类型是用户定义的复合类型&#x…

填报表中也可以添加 html 事件

在实际的项目开发中&#xff0c;填报表的应用十分广泛。 多数情况下&#xff0c;填报表会作为整个项目的一部分配合需求灵活使用&#xff0c;但有时也会受大项目环境的影响&#xff0c;产生一些特别的要求。比如&#xff0c;通常报表单元格的数据类型大多是文本&#xff0c;有时…

60+业内技术专家,9大核心技术专题,AI ProCon倒计时一周!

2018 年&#xff0c;由 CSDN 举办的第一届 AI 开发者大会喊出“只讲技术&#xff0c;拒绝空谈”&#xff0c;两天会议时间&#xff0c;国内外几十家顶尖科技企业讲述了其主流技术及其应用案例&#xff0c;真正引领国内开发者紧跟技术浪潮。一年过去&#xff0c;在你还未有所觉察…

密码学研究-数字签名

引入&#xff1a;提到签名&#xff0c;大家都不陌生&#xff0c;大家知道&#xff0c;重大的文件一般都要领导签名&#xff0c;来确保这个文件的真实有效。而一些比较重要的合同&#xff0c;比如买房的购房合同&#xff0c;都要盖“骑缝章”&#xff0c;这个骑缝章&#xff0c;…

C++11中shared_ptr的使用

在C中&#xff0c;动态内存的管理是通过一对运算符来完成的&#xff1a;new&#xff0c;在动态内存中为对象分配空间并返回一个指向该对象的指针&#xff0c;可以选择对对象进行初始化&#xff1b;delete&#xff0c;接受一个动态对象的指针&#xff0c;销毁该对象&#xff0c;…

colly源码学习

colly源码学习 colly是一个golang写的网络爬虫。它使用起来非常顺手。看了一下它的源码&#xff0c;质量也是非常好的。本文就阅读一下它的源码。 使用示例 func main() {c : colly.NewCollector()// Find and visit all linksc.OnHTML("a[href]", func(e *colly.HTM…

可惜了,你们只看到“双马会”大型尬聊

作者 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;导读&#xff1a;2019 年 8 月 29 日&#xff0c;世界人工智能大会&#xff08;WAIC&#xff09;在上海正式拉开帷幕。开幕式上&#xff0c;最让人瞩目的莫过于阿里巴巴前 CEO 马云与特斯拉 CEO Elon Musk …

Java 过滤特殊字符的 正则表达式

Java正则表达式学习&#xff1a; 因为正则表达式是一个很庞杂的体系&#xff0c;此例仅举些入门的概念&#xff0c;更多的请参阅相关书籍及自行摸索。 \\ 反斜杠 \t 间隔 (\u0009) \n 换行 (\u000A) \r 回车 (\u000D) \d 数字 等价于[0-9] \D 非数字 等价于[^0-9] \s 空…

C++11中unique_ptr的使用

在C中&#xff0c;动态内存的管理是通过一对运算符来完成的&#xff1a;new&#xff0c;在动态内存中为对象分配空间并返回一个指向该对象的指针&#xff0c;可以选择对对象进行初始化&#xff1b;delete&#xff0c;接受一个动态对象的指针&#xff0c;销毁该对象&#xff0c;…

从这篇YouTube论文,剖析强化学习在工业级场景推荐系统中的应用

作者 | 吴海波转载自知乎用户吴海波【导读】本文作者根据两篇工业界背景的论文解答了 RL 在推荐场景需要解决的问题与困难&#xff0c;以及入门需要学习得相关知识点。2 个月前&#xff0c;业界开始流传 youtube 成功将 RL 应用在了推荐场景&#xff0c;并且演讲者在视频中说是…

java中两个Integer类型的值相比较的问题

转载自&#xff1a; https://www.cnblogs.com/xh0102/p/5280032.html 两个Integer类型整数进行比较时&#xff0c;一定要先用intValue()方法将其转换为int数之后再进行比较&#xff0c;因为直接使用比较两个Integer会出现问题。 总结&#xff1a; 当给Integer直接赋值时&#x…

C#共享内存实例 附源码

原文 C#共享内存实例 附源码 网上有C#共享内存类&#xff0c;不过功能太简单了&#xff0c;并且写内存每次都从开头写。故对此进行了改进&#xff0c;并做了个小例子&#xff0c;供需要的人参考。 主要改进点&#xff1a; 通过利用共享内存的一部分空间(以下称为“数据信息区”…

C++11中weak_ptr的使用

在C中&#xff0c;动态内存的管理是通过一对运算符来完成的&#xff1a;new&#xff0c;在动态内存中为对象分配空间并返回一个指向该对象的指针&#xff0c;可以选择对对象进行初始化&#xff1b;delete&#xff0c;接受一个动态对象的指针&#xff0c;销毁该对象&#xff0c;…

经典不过时,回顾DeepCompression神经网络压缩

作者 | 薰风初入弦转载自知乎导读&#xff1a;本文作者为我们详细讲述了 ICLR 2016 的最佳论文 Deep Compression 中介绍的神经网络压缩方法。神经网络压缩一直是一个重要的研究方向&#xff0c;而目前业界最认可的压缩方法莫过于 ICLR 2016 的最佳论文 Deep Compression&#…

区块链技术特点之去中心化特性

想知道更多关于区块链技术知识&#xff0c;请百度【链客区块链技术问答社区】 链客&#xff0c;有问必答&#xff01;&#xff01; 由于区块链技术去中心化的特性&#xff0c;其在我们生活中的很多重要领域&#xff08;如金融、管理&#xff09;等方面具有重要的意义。例如&…

Android APK反编译

转自&#xff1a;http://blog.csdn.net/ithomer/article/details/6727581 一、Apk反编译得到Java源代码 下载上述反编译工具包&#xff0c;打开apk2java目录下的dex2jar-0.0.9.9文件夹&#xff0c;内含apk反编译成java源码工具&#xff0c;以及源码查看工具。 apk反编译工具dex…

Java泛型进阶 - 如何取出泛型类型参数

在JDK5引入了泛型特性之后&#xff0c;她迅速地成为Java编程中不可或缺的元素。然而&#xff0c;就跟泛型乍一看似乎非常容易一样&#xff0c;许多开发者也非常容易就迷失在这项特性里。多数Java开发者都会注意到Java编译器的类型擦除实现方式&#xff0c;Type Erasure会导致关…

C++11中override的使用

override是C11中的一个继承控制关键字。override确保在派生类中声明的重载函数跟基类的虚函数有相同的声明。 override明确地表示一个函数是对基类中一个虚函数的重载。更重要的是&#xff0c;它会检查基类虚函数和派生类中重载函数的签名不匹配问题。如果签名不匹配&#xff…

平头哥发布一站式芯片设计平台“无剑”,芯片设计成本降低50%

导读&#xff1a;8 月 29 日&#xff0c;在上海举行的世界人工智能大会上&#xff0c;阿里巴巴旗下半导体公司平头哥发布 SoC 芯片平台“无剑”。无剑是面向 AIoT 时代的一站式芯片设计平台&#xff0c;提供集芯片架构、基础软件、算法与开发工具于一体的整体解决方案&#xff…

Windows XP下,JDK环境变量配置

2019独角兽企业重金招聘Python工程师标准>>> 1.安装JDK&#xff0c;安装过程中可以自定义安装目录等信息&#xff0c;例如我们选择安装目录为D:\java\jdk1.5.0_08&#xff1b; 2.安装完成后&#xff0c;右击“我的电脑”&#xff0c;点击“属性”&#xff1b; 3.选择…

Markdown语法简介

Markdown是一种方便记忆、书写的纯文本标记语言&#xff0c;用户可以使用这些标记符号以最小的输入代价生成极富表现力的文档。它目标是实现易读易写。Markdown的语法全由一些符号所组成。Markdown语法的目标是成为一种适用于网络的书写语言。 Markdown优点&#xff1a;纯文本…

吴恩达:AI未来将呈现四大发展趋势

作者 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;导读&#xff1a;8 月 30 日&#xff0c;世界人工智能大会精彩继续。在今天的全球工业智能峰会上&#xff0c;Landing.AI 创始人及首席执行官吴恩达来到现场&#xff0c;做了题为《人工智能是新电力》的演讲…

嵌入式课程安排 嵌入式培训课程大纲参考

嵌入式是一门综合性的学科&#xff0c;现在学习嵌入式开发不是单纯局限于单片机或者Linux&#xff0c;嵌入式课程中包含着非常多的内容。以粤嵌嵌入式课程进行参考&#xff0c;看看我们要学习嵌入式的话&#xff0c;要掌握哪些必备的技能。嵌入式课程安排包含&#xff1a;1、入…

Linux网站架构系列之Apache----进阶篇

本篇博文为Linux网站架构系列之apache的第二篇&#xff0c;我将带大家一起学习apache的编译参数&#xff0c;目录结构和配置文件等方面的知识&#xff0c;实现对apache服务的进一步掌握&#xff0c;并使之能更好的应用到生产实战中去。一、编译参数在上篇的apache部署中&#x…

仅用10天设计的JavaScript,凭什么成为程序员最受欢迎的编程语言?

导语&#xff1a;在这个世纪之交诞生的 JavaScript&#xff0c;没人想到会发展为当今世界上最流行的语言之一。它不够成熟&#xff0c;不够严肃&#xff0c;甚至连名字都是模仿的 Java。那么&#xff0c;JavaScript 的成功是依靠运气和完美时机的侥幸吗&#xff1f;其实不然——…

C++11中= delete;的使用

C11中&#xff0c;对于deleted函数&#xff0c;编译器会对其禁用&#xff0c;从而避免某些非法的函数调用或者类型转换&#xff0c;从而提高代码的安全性。 对于 C 的类&#xff0c;如果程序员没有为其定义特殊成员函数&#xff0c;那么在需要用到某个特殊成员函数的时候&…

vue 使用scss

使用vue-cli模板创建的项目中&#xff0c;使用scss步骤 1. cmd命令&#xff1a; cnpm install sass-loader --save-devcnpm install node-sass --sava-dev2.查看package.json文件中是否已自动添加以下信息 3. 转载于:https://www.cnblogs.com/duanzhenzhen/p/10453495.html

EBS form日历可选范围设置(calendar.setup )介绍

Calendar是Template提供给我们的standard object.可以使我们方便的为日期型字段提供日期的选择列表.form中设置日历方法:1. 为日期型字段指定LOV(ENABLE_LIST_LAMP)2. 在字段的KEY–LISTVAL事件中编写代码:Calendar.showCalendar Package包含如下几个Procedure:1. Calendar.sho…