Search code examples
node.jsshellchild-processspawn

Running a shell script from Nodejs


I have a shell script start.sh whose job is to spawn multiple java processes in the background and then exit normally like so:

java -jar jar1.jar &
java -jar jar2.jar &
...
exit 0

A bunch of output is printed:

Starting jar1
Starting jar2
...

Now I want nodejs to invoke start.sh keep track of the output so I can act on it when starter.sh is done

Problem is Nodejs considers starter.sh to be done only when all the java processes that have been started are dead. Which is weird because from a shell, starter.sh exits right away and does not have any relations with the java processes anymore

How I can make starter.sh behave the same way when invoked from node?

This is the code I use to spawn starter.sh:

  const process = spawn('starter.sh', []);

  process.stdout.on('data', (data) => {
    console.log(`stdout: ${data}`);
    // prints 'Starting jarx' like expected
  });

  process.stderr.on('data', (data) => {
    console.log(`stderr: ${data}`);
  });

  process.on('close', (code) => {
    console.log(`child process exited with code ${code}`);
    // Not called unless all the spawned java processes are dead :(
  });

Solution

  • I a have feeling that my problem is related to the child processes sharing stdio with the node parent. Indeed where would the java processes write if they didn't have a stdout to write to. Thus node has to wait for them to be done to consider starter.sh done

    I have settled for giving the child process it's own stdio and write starter.sh output to a file:

      const out = fs.openSync('/tmp/command.out', 'w');
      const err = fs.openSync('/tmp/command.err', 'w');
    
      const process = spawn(command, args, {detached: true, stdio: ['ignore', out , err]});
      process.unref();
    
      process.on('close', (code) => {
        console.log(`child process exited with code ${code}`);
        console.log(fs.readFileSync('/tmp/command.out', 'utf8');
        console.log(fs.readFileSync('/tmp/command.err', 'utf8'));
      });
    

    That way the close call back is called as soon as starter.sh is done and I can still access it's output.