在 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);
}

扩大浏览

迎接继承浏览本专栏别的高赞文章:

本文首发微信民众号:jingchengyideng

迎接扫描二维码关注民众号,天天都给你推送新颖的前端手艺文章

《在 Node.js 顶用子历程操纵规范输入/输出》

    原文作者:疯狂的技术宅
    原文地址: https://segmentfault.com/a/1190000018265031
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞