简体   繁体   中英

How to pipe and save output of spawnSync process in Node.js?

I spawn some command synchronously and want two things:

  1. Pipe its stdout to process.stdout.
  2. Save the stdout into variable.

I've written this code:

var spawnSync = require('child_process').spawnSync;
var result = spawnSync('ls', [ '-l', '-a' ]);
var savedOutput = result.stdout;

console.log(String(savedOutput));

So, I store stdout in savedOutput variable — it's ok, and log it out. But I haven't pipe it to stdout. If the spawned process is long and write strings one by one, I see empty screen a long time, and at the end I see whole stdout of process.

I've add options for piping:

var spawnSync = require('child_process').spawnSync;
var result = spawnSync('ls', [ '-l', '-a' ], {
    stdio: [ 'ignore', 1, 2 ]
});
var savedOutput = result.stdout;

console.log(String(savedOutput));

Stdout of spawned process is piped to stdout — it's ok. But result.stdout is empty.

I've tried to use stream:

var spawnSync = require('child_process').spawnSync;
var stream = require('stream');
var grabber = new stream.Writable();

grabber._write = function(chunk, enc, done) {
    console.log('Chunk:');
    console.log(String(chunk));
    done();
};

var result = spawnSync('ls', [ '-l', '-a' ], {
    stdio: [ 'ignore', grabber, 2 ]
});

... but get an error:

internal/child_process.js:795
  throw new TypeError('Incorrect value for stdio stream: ' +
  ^

TypeError: Incorrect value for stdio stream: Writable

If I set grabber.fd = 2 , I don't get an error, but child stdout pipes to stdout instead of grabber.

So. How to save child stdout into variable and pipe it to stdout in the same time?

Does this solve your problem?

var spawnSync = require('child_process').spawnSync;
var result = spawnSync('ls', [ '-l', '-a' ], {
    cwd: process.cwd(),
    env: process.env,
    stdio: 'pipe',
    encoding: 'utf-8'
});
var savedOutput = result.stdout;

console.log(String(savedOutput));

There are a couple of possible solutions, both involving spawnSync 's options .

There is a live version of this code available at https://repl.it/repls/MundaneConcernedMetric

const spawnSync = require('child_process').spawnSync;

// WITH NO OPTIONS:
// stdout is here, but is encoded as 'buffer'
const result_noOptions = spawnSync('ls', [ '-l', '-a' ]); // won't print anything
console.log(result_noOptions.stdout); // will print <Buffer ...> on completion

// WITH { encoding: 'utf-8'} OPTIONS:
// stdout is also here, _but only printed on completion of spawnSync_
const result_encoded = spawnSync('ls', [ '-l', '-a' ], { encoding: 'utf-8' }); // won't print anything
console.log(result_encoded.stdout); // will print ls results only on completion

// WITH { stdio: 'inherit' } OPTIONS:
// there's no stdout, _because it prints immediately to your terminal_
const result_inherited = spawnSync('ls', [ '-l', '-a' ], { stdio: 'inherit'}); // will print as it's processing
console.log(result_inherited.stdout); // will print null

In addition to stdout , you can also get: pid , output , stdout , stderr , status , signal , & error .

https://nodejs.org/api/child_process.html#child_process_child_process_spawnsync_command_args_options

I think the problem is that "spawnSync" is synchronous, and does not return control to node until the child process has exited. This means node does no processing during the execution of the child, and thus cannot see any of the intermediate output from the child.

You need to use the non-"sync" spawn. This will have additional complications in that your code is async, and you need to wait for the 'close' event to be sure the child data is collected. And you need to figure out what to do with stderr. Something like this:

const spawn = require('child_process').spawn;
const lsChild = spawn('ls', [ '-l', '-a' ]);

let savedOutput = '';

lsChild.stdout.on('data', data => {
   const strData = data.toString();
   console.log(strData);
   savedOutput += strData;
});

lsChild.stderr.on('data', data => {
   assert(false, 'Not sure what you want with stderr');
});

lsChild.on('close', code => {
   console.log('Child exited with', code, 'and stdout has been saved');
   // at this point 'savedOutput' contains all your data.
});

stdout was not helping to get output for spawnSync, so used this instead for storing output in a variable. I'm using Windows cmd

var cp = require('child_process');

var ls = cp.spawnSync('cmd.exe', ['/c', 'my.bat']);

    var output = result.output.toString();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM