翻譯:瘋狂的技術宅
原文: http://2ality.com/2018/05/chi...
本文首發微信公衆號:jingchengyideng
歡迎關注,天天都給你推送新鮮的前端技術文章html
在本中,咱們在 Node.js 中把 shell 命令做爲子進程運行。而後異步讀取這些進程的 stdout 並寫入其 stdin。前端
首先從在子進程中運行 shell 命令開始:node
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();
解釋:git
咱們用了 spawn()
,它能夠使咱們在命令運行時訪問命令的 stdin,stdout 和 stderr。程序員
函數 onExit()
以下所示。github
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
異步執行並暫停,以免緩衝區被消耗太多。
解釋:shell
spawn()
經過 sink.stdin
('pipe'
)訪問 stdin。 stdout 和 stderr 被轉發到 process.stdin
和 process.stderr
,如前面所述。await
寫完成。而是 await
子進程 sink
完成。接下來了解 streamWrite()
的工做原理。segmentfault
Node.js 寫流的操做一般涉及回調(參見文檔)。代碼以下。api
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)) } }
解釋:
process.stderr
。awat
直到 echoReadable()
完成。沒有這個 await
,DONE
將會在調用 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); }