Format Node Stdout Pipe - node.js

I've got the following setup to run a number of spawned processes:
spawn(proc, args, {
env: process.env,
cwd: process.env.HOME,
stdio: [ 'pipe', process.stdout, process.stdout ]
});
My curiosity lies in the output streams. I'm curious if there is a way to have the output indented 2 characters? I'd like to have the process' output clearly distinguished.

Related

How to get full stdout from a node.js child_process that spawns another process recursively?

I'm trying to spawn a child process like this:
const { spawn } = require('child_process')
const child = spawn("python run.py", {
detached: true,
shell: true,
})
The problem is, the run.py then calls another process called child.py.
I noticed that when I listen to the child's stdout, it prints all the log messages in the python run.py process, but not the python child.py, which is called interanlly by run.py.
So my question is, how do you capture all the STDOUT from child processes recursively?
The intermediate run.py python determines where stdio from child.py goes.
Here's some JS to run the python, setting specific stdio options
const { spawn } = require('node:child_process')
console.log('python.js')
const child = spawn("./run.py", {
stdio: [ 'inherit', 'inherit', 'inherit', 'pipe' ]
})
child.stdio[3].on('data', (data) => {
console.log('fd3: %s', data)
})
The stdio option, inherit is used to pass through the node processes stdin, stdout, stderr. This setup is not specific to your question, but I've included it as an example of what run.py will do with it's child.
The 4th element pipe opens an extra channel as file descriptor 3 (After descriptor 0/1/2 are used for stdin/stdout/stderr.
The intermediate run.py python then chooses where stdio goes for its child.py processes.
#!/usr/local/bin/python3
from subprocess import Popen
from os import fdopen
print('run.py')
# `Popen` defaults to `inherit`, so stdout/stderr from child.py will
# be written to the `run.py` processes stdout/sterr
Popen(['./child.py'])
# Second child, the file descriptor created in node will be used for stdout
pipe = fdopen(3)
Popen(['./child.py'], stdout=pipe)
The child script outputs as normal.
#!/usr/local/bin/python3
print('child.py')
When run:
$ node python.js
python.js
run.py
child.py
fd3: child.py

nodejs run a bash script using spawn

I know this question has been asked many times but I have tried most of the methods and they just don't work for me.
So here is my problem, I have a simple bash script like this
#!/bin/bash
echo "Username: $1";
echo %DATABASE_URL%;
I want to run this script in a separate process. so if the parent process gets killed during my script being excused it still continues running.
Here is my nodejs code
const child = spawn('bash', [`script.sh`, 'test'], {
detached: true,
cwd: process.cwd(),
detached: true,
stdio: "inherit",
DATABASE_URL: 'test'
}, function (err, stdout, stderr) {
// Node.js does not invoke this
console.log(stdout);
stdout.on("data", data => {
console.log('Output of script execution');
});
stderr.on("data", data => {
console.log('an error with file system');
});
});
child.unref();
child.on('exit', (code) => {
console.log("Child exited");
});
So I know that my script returns some output and should run callback but it does not run it. It directly jumps to the on.('exit') callback which confuses me.
Also it worth mentioning that I am testing the code on windows and bash script.sh 'test' works if I run it on cmd.
Posts I have tried:
How to run shell script file using nodejs?
Execute script from Node in a separate process
Bash Script : what does #!/bin/bash mean?
and many of the existing weblogs that explains the same.

'Invalid Verb' error only when Calling Inkscape as a subprocess

When I call the following in the command line it works like a charm:
inkscape --with-gui --batch-process --export-filename=- \
--actions="select-all;ObjectToPath" \
/full/path/to/example.svg
But when I open Node.js and make the same call in a subprocess like so:
const cp = require("child_process");
var child = cp.spawn(
"/usr/bin/inkscape",
[
"--with-gui",
"--batch-process",
"--export-filename=-",
'--actions="select-all;ObjectToPath"',
"/full/path/to/example.svg",
],
{
cwd: process.cwd(),
detached: true,
stdio: "inherit",
}
);
I get the following error:
Unable to find: "select-all
verbs_action: Invalid verb: "select-all
Unable to find: ObjectToPath"
verbs_action: Invalid verb: ObjectToPath"
and the file is returned (printed to stdout) unchanged. Any Idea why the verbs are not found when running Inkscape as a subprocess but not calling it directly from the terminal? I get this same error on ubuntu (20.04) and OSX using the latest Inkscape (1.0.1+r73).
When you use cp.spawn with an array of arguments, you don't need to internally quote "select-all;ObjectToPath" like you would with a shell. (In a shell, the quotes prevent the shell from tokenizing the command line into two lines. Due to the same mechanism - or lack thereof - attempting to use shell variables such as $$ or environment variables such as $PATH would fail when you use cp.spawn, since there's nothing to parse that.)
I would imagine
const cp = require("child_process");
var child = cp.spawn(
"/usr/bin/inkscape",
[
"--with-gui",
"--batch-process",
"--export-filename=-",
"--actions=select-all;ObjectToPath",
"/full/path/to/example.svg",
],
{
cwd: process.cwd(),
detached: true,
stdio: "inherit",
},
);
would do the trick for you.

Redirect stdout/stderr from a child_process to /dev/null or something similar

I am creating some child_processes with Node.js (require('child_process')) and I want to ensure that the stdout/stderr from each child_process does not go to the terminal, because I want only the output from the parent process to get logged. Is there a way to redirect the stdout/stderr streams in the child_processes to /dev/null or some other place that is not the terminal?
https://nodejs.org/api/child_process.html
perhaps it's just:
var n = cp.fork('child.js',[],{
stdio: ['ignore','ignore','ignore']
});
I just tried that, and that didn't seem to work.
Now I tried this:
var stdout, stderr;
if (os.platform() === 'win32') {
stdout = fs.openSync('NUL', 'a');
stderr = fs.openSync('NUL', 'a');
}
else {
stdout = fs.openSync('/dev/null', 'a');
stderr = fs.openSync('/dev/null', 'a');
}
and then this option:
stdio: ['ignore', stdout, stderr],
but that didn't do it, but it seems like using the "detached:true" option might make this work.
Solution:
To throw away the stdout and stderr of a forked childprocess:
setup a pipe i.e. use silent = True when forking.
And redirect the stdout and stderr pipes on the parent process into /dev/null.
Explanation:
The node.js documentation states :
For convenience, options.stdio may be one of the following strings:
'pipe' - equivalent to ['pipe', 'pipe', 'pipe'] (the default)
'ignore' - equivalent to ['ignore', 'ignore', 'ignore']
'inherit' - equivalent to [process.stdin, process.stdout, process.stderr] or [0,1,2]
Apparently childprocess.fork() does NOT support ignore; Only childprocess.spawn() does.
fork does support a silent option that allows one to choose between pipe OR inherit.
When forking a child process:
If silent = True, then stdio = pipe.
If silent = False, then stdio = inherit.
silent
Boolean
If true, stdin, stdout, and stderr of the child will be piped to the parent, otherwise they will be inherited from the parent.
See the 'pipe' and 'inherit' options for child_process.spawn()'s stdio for more details.

Pipe Node stdout in realtime

I have a spawn process running a docker pull and I'm using the following:
const proc = spawn('docker', [ 'pull', 'some/container' ], { env: process.env, cwd: process.env.HOME })
proc.stdout.pipe(process.stdout)
As it runs it breaks up and downloads the individual SHA's and the above works pretty well, however, it puts each response on a new line. I'm curious if there's a way to emulate the "normal" output so each line writes as it pull the image.
If you're just piping to process.stdout, then you could just set the stdio option like:
const proc = spawn('docker', [
'pull',
'some/container'
], {
env: process.env,
cwd: process.env.HOME,
stdio: ['pipe', process.stdout, 'pipe']
});
The end result being that docker will now see its stdout as a TTY (assuming this node script is being run from a terminal/pty of course) and not a pipe.

Resources