Reputation: 3598
Is there an easy way of capturing child_process
spawnSync
or execSync
stdout/stderr? I have seen other posts say a few years ago that it wasn't possible at the time since it was synchronous.
I have a problem where I need to capture another threads output so I can parse what is sent to stdout. The problem is, I haven't found anything past just using spawn
and use the stdout.on
events.
This causes issues since my program isn't meant to be asynchronous.
Edit:
The tool I have is called eslint-watch. It adds functionality to eslint like file watching and specifies a default directory. The problem is in this discussion https://github.com/eslint/eslint/issues/2831 we came to the conclusion that I needed to execute eslint as a binary and grab the help options from it.
After eslint returns its help options I parse them and funnel them into opinionator. So my wrapper has the same help context menu as eslint but also has my commands in there. Problem is grabbing the help is one of the first things I do so I can parse the commands from the terminal.
Upvotes: 3
Views: 15454
Reputation: 113
Things have changed since your edit.
execSync now returns stdout on completion.
//will return date() on *nix systems;
console.log(require("child_process").execSync(`date`).toString());
>>> Fri Jul 10 14:19:41 UTC 2020
Upvotes: 6
Reputation: 115
I'm not sure if I understand correctly and would guess an anwser is of no interrest to poster anymore. However since I struggled with something similar here some example:
var karma = spawnSync("node",
["node_modules/karma/bin/karma", "start", "karma.conf.js", "--single-run"],
{
stdio: [null, process.stdout, process.stderr]
}
);
if (karma.status != 0) {
console.log(`Karma reported error(s) (${karma.status}). Build aborted\n`.red);
process.exit(1);
} else {
console.log(`Karma success\n`.green);
}
The above example starts Karma and performs the tests specified in the config file. What I want with this is to force perform a test when webpack is set to do a release build. The idea is that the build server cannot perform a release without testing first, even if the build is missconfigured.
This ment introducing the above block into the webpack.conf.js and blocking the executing thread until karma is done, meanwhile piping karmas stdout/err onto the console. Using spawnSync instead of spawn is only neccessary since once the thread returns from the webpack.conf.json Webpack immediately starts and potentially build the project with failing tests.
What is described in the original question sound like an asynchronous thing to me. Call the process and once stdout arrives add your additional output in an event handler. If you still wan't to do it synchronously, the stdio array just takes streams. This means you could create a wrapper stream for stdout and tell the child-process to use that.
Upvotes: 3
Reputation: 17357
Referring to require('child_process').spawnSync() or .exexSync()
, the Node API docs state pretty clearly that:
These methods are synchronous, meaning they WILL block the event loop, pausing execution of your code until the spawned process exits.
Blocking calls like these are mostly useful for simplifying general purpose scripting tasks and for simplifying the loading/processing of application configuration at startup.
So, you're correct. It is not possible to process stdio events while running an external process synchronously because the Node event loop is halted until the external process completes.
You might consider capturing the processes stdout
and/or stderr
to one or more files using shell output redirection (i.e. using bash: COMMAND 1>stdout.txt 2>stderr.log
) which you would do by running your command in the bash shell.
The simplest way to do this from Node would be to:
file:runner.sh
#/bin/sh
PATH_TO_YOUR_COMMAND_HERE 1>command_stdout.txt 2>command_stderr.txt
file:nodeprocess.js
var fs = require('fs'),
spawnSync = require('child_process').spawnSync,
args = ['/bin/sh', 'runner.sh'];
spawnSync(args); // Event Loop will wait here.
if(fs.existsSync('./command_stdout.txt')){
// process standard output from your command here
}
if(fs.existsSync('./command_stderr.txt')){
// process error output from your command here
}
Of course, you should do all the proper file system hygiene things, like writing your output files into /tmp
and removing them after you're done with them, but I leave those things to your imagination.
I'm curious why you cannot use .spawn()
or .fork()
since, if a command can be run from the command line, there should be no reason it cannot be run asychronously from Node.
Upvotes: 3