Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 490
isqua Avatar asked Mar 17 '16 17:03

isqua


People also ask

What is child_process spawn?

The node:child_process module provides the ability to spawn subprocesses in a manner that is similar, but not identical, to popen(3) . This capability is primarily provided by the child_process. spawn() function: const { spawn } = require('node:child_process'); const ls = spawn('ls', ['-lh', '/usr']); ls. stdout.

What is spawn and fork in node JS?

Spawn is useful when you want to make a continuous data transfer in binary/encoding format — e.g. transferring a 1 Gigabyte video, image, or log file. Fork is useful when you want to send individual messages — e.g. JSON or XML data messages.


2 Answers

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.
});
like image 85
Alex Tullenhoff Avatar answered Sep 19 '22 18:09

Alex Tullenhoff


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));
like image 40
yeti Avatar answered Sep 18 '22 18:09

yeti