博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在 Node.js 中用子进程操作标准输入/输出
阅读量:5826 次
发布时间:2019-06-18

本文共 4103 字,大约阅读时间需要 13 分钟。

翻译:疯狂的技术宅
原文:

本文首发微信公众号: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
{ 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); }); });}

子进程的实现

以下代码用 异步写入以 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
{ 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);}

扩展阅读

  • 博客:“”
  • “探索ES2018和ES2019”中的“ 一章
  • “探索ES2016和ES2017”中的“” 一章

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


本文首发微信公众号:jingchengyideng

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

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


转载地址:http://kasdx.baihongyu.com/

你可能感兴趣的文章
python ipaddress模块使用
查看>>
统计文件里面某个字符串出现次数
查看>>
FHS
查看>>
文件权限
查看>>
云从科技发布3D结构光人脸识别技术
查看>>
busybox里的僵尸进程为何那么多
查看>>
appium自动化属性使用一
查看>>
python debug
查看>>
mkisofs
查看>>
java 连接数据库之一个完整的函数
查看>>
centos5.6下virtualbox安装手记
查看>>
mysql脚本
查看>>
OllyDBG 入门系列教学--让你瞬间成为破解高手
查看>>
jQuery插件开发的准备
查看>>
Dubbo点滴(2)之集群容错
查看>>
Zend Framework 自动加载类的实现方法
查看>>
7月24日云栖精选夜读:未来的超级智能网络攻击需要AI竞技俱乐部来拯救
查看>>
Cloudera携手CenturyLink提供大数据即服务
查看>>
所有代码都需要单元测试覆盖吗?
查看>>
如何创建线程
查看>>