资讯专栏INFORMATION COLUMN

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

leeon / 320人阅读

摘要:在行中,我们将子进程的连接到当前进程的。等待子进程通过退出函数如下所示。子进程的实现以下代码用异步写入以命令运行的子进程的我们为命令生成一个名为的独立进程。而是子进程完成。没有这个,将会在调用之前被输出。

翻译:疯狂的技术宅
原文: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 {
  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
");
  await streamWrite(writable, "Second line
");
  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);
}
扩展阅读

博客:“通过 Node.js 的异步迭代读取流”

“探索ES2018和ES2019”中的“异步迭代 一章

“探索ES2016和ES2017”中的“异步功能” 一章

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

12个令人惊叹的CSS实验项目

世界顶级公司的前端面试都问些什么

CSS Flexbox 可视化手册

过节很无聊?还是用 JavaScript 写一个脑力小游戏吧!

从设计者的角度看 React

CSS粘性定位是怎样工作的

一步步教你用HTML5 SVG实现动画效果

程序员30岁前月薪达不到30K,该何去何从

第三方CSS安全吗?

谈谈super(props) 的重要性

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102134.html

相关文章

  • Node.js学习之路14——Process进程

    摘要:在中,只支持单线程。在这种场合下,如果能够使用多进程,则可以为每个请求分配一个进程,从而可以更好地使用服务器端的资源。进程进程对象的属性用于运行应用程序的可执行文件的绝对路径的版本号及其各依赖的版本号当前运行的平台用于读入标准输入流的对象。 Process 在Node.js中,只支持单线程。但是在应用程序中,如果只使用单线程进行操作,从接收请求开始到返回响应为止的这段时间内可能存在很长...

    darry 评论0 收藏0
  • Node.js学习之路08——fs文件系统之stream流的基本介绍

    摘要:中各种用于读取数据的对象对象描述用于读取文件代表客户端请求或服务器端响应代表一个端口对象用于创建子进程的标准输出流。如果子进程和父进程共享输入输出流,则子进程的标准输出流被废弃用于创建子进程的标准错误输出流。 9. stream流 fs模块中集中文件读写方法的区别 用途 使用异步方式 使用同步方式 将文件完整读入缓存区 readFile readFileSync 将文件部...

    BoYang 评论0 收藏0
  • Node.js中spawn与exec的异同比较

    摘要:返回值对象利用给定的命令以及参数执行一个新的进程,如果没有参数数组,那么将默认是一个空数组。当子进程执行完毕后将会执行的回调函数,参数有返回值对象在中运行一个命令,并缓存命令的输出。 前言 众所周知,Node.js在child_process模块中提供了spawn和exec这两个方法,用来开启子进程执行指定程序。这两个方法虽然目的一样,但是既然Node.js为我们提供了两个方法,那它...

    garfileo 评论0 收藏0
  • 深入理解Node.js 进程与线程(8000长文彻底搞懂)

    摘要:在单核系统之上我们采用单进程单线程的模式来开发。由进程来管理所有的子进程,主进程不负责具体的任务处理,主要工作是负责调度和管理。模块与模块总结无论是模块还是模块,为了解决实例单线程运行,无法利用多核的问题而出现的。 前言 进程与线程是一个程序员的必知概念,面试经常被问及,但是一些文章内容只是讲讲理论知识,可能一些小伙伴并没有真的理解,在实际开发中应用也比较少。本篇文章除了介绍概念,通过...

    Harpsichord1207 评论0 收藏0
  • Node.js 全局对象 process

    摘要:是一个全局变量,对象的属性。的源码启动进程,评估时返回函数失败。调用监听器回调函数时会将的值作为唯一参数传入。信号列表详见标准的信号名,如等。返回一个对象,描述了进程所用的内存状况,单位为字节。一旦当前事件循环结束,调用回调函数。 process是一个全局变量,global对象的属性。它的作用是描述当前Node.js进程状态的对象,提供了一个与操作系统的简单接口。通常在你写本地命令程序...

    JasonZhang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<