Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prefix every line of the output of a child_process spawn() call with text?

I execute several processes using spawn() from child_process.

const { spawn } = require('child_process');

spawn("sh", ["script1.sh"], { shell: false, stdio: "inherit"});
spawn("sh", ["script2.sh"], { shell: false, stdio: "inherit"});
spawn("sh", ["script3.sh"], { shell: false, stdio: "inherit"});

Question: How to prefix the output of the scripts with text?

The idea is that I would be able to easily distinguish what each script logs.

I went through the spawn documentation but couldn't find anything that would help achieve this.

Note: I cannot modify the scripts.

like image 631
Sasha Shpota Avatar asked Dec 01 '20 14:12

Sasha Shpota


People also ask

What is child_process spawn?

spawn() or child_process. spawnSync() . child_process. exec() : spawns a shell and runs a command within that shell, passing the stdout and stderr to a callback function when complete.

How do you spawn in Javascript?

The spawn function launches a command in a new process and we can use it to pass that command any arguments. For example, here's code to spawn a new process that will execute the pwd command. const { spawn } = require('child_process'); const child = spawn('pwd');

What is a child_process module in node JS?

Usually, Node. js allows single-threaded, non-blocking performance but running a single thread in a CPU cannot handle increasing workload hence the child_process module can be used to spawn child processes. The child processes communicate with each other using a built-in messaging system.


2 Answers

You have stdio set to "inherit".

If you set this to "pipe", Node.js will give you back readable streams for stderr and stdin, which you can use to add your prefix.

const { spawn } = require('child_process');

const one = spawn("sh", ["script1.sh"], { shell: false, stdio: "pipe"});
let oneStdout = ''
one.stdout.on('data', function (chunk) {
  oneStdout += chunk
  const lines = oneStdout.split('\n')
  while(lines.length > 1) {
    const line = lines.shift()
    console.log('one',line)
  }
  oneStdout = lines.shift()
})
one.stdout.on('end', function () {
  console.log('one', oneStdout)
})

Here is the relevant section in the docs: https://nodejs.org/api/child_process.html#child_process_subprocess_stdio

Potential gotcha:

When "prefixing" you likely want to prefix each new line but not all scripts write to stdout a full line at a time. Play around with a few scripts that use echo -n "foobar" throughout the output to test that you're handling line breaks correctly.

like image 188
retrohacker Avatar answered Nov 14 '22 22:11

retrohacker


Here is how I run external commands and capture their output, e.g. to prefix each line with a timestamp:

const { spawn } = require("child_process"),
  command = "echo",
  args = ["Studies have shown that people watch TV more than any other appliance."];

const child = spawn(command, args);

child.stdout.on('data', buff => {
  const line = buff.toLocaleString();
  console.info(new Date(), line);
});
child.stderr.on('data', buff => { // also catch any error output
  const line = buff.toLocaleString();
  console.error(new Date(), line);
});
child.on('exit', code => {console.info(`Command exited with code ${code}`)});

Here's what it looks like when you run it:

$ node thatScript.js 
2020-12-10T11:46:51.455Z Studies have shown that people watch TV more than any other appliance.

Command exited with code 0
$ 
like image 34
Jeremy Jones Avatar answered Nov 14 '22 22:11

Jeremy Jones