If you need a command to happen after another, you can use spawnSync and other *Sync functions in the child_process module. However, the command's output will not be printed to the terminal as it runs: you might decide to read the return value and send the command's output to your program's output yourself, but all output will be bunched up and printed at once.
const { execSync } = require("child_process");
const process = require("process");
process.stdout.write(execSync("echo one; sleep 1; echo two; sleep 1").toString())
console.log("This must happen last.");
For reference, this is what I want to happen:
echo one
sleep 1
echo two
sleep 1
echo "This must happen last."
To actually send the child process's output to your program's output as they come, you have to use the asynchronous version and attach a handler on it. But now things get out of order.
const { spawn } = require("child_process");
const process = require("process");
function cmd(...command) {
let p = spawn(command[0], command.slice(1));
p.stdout.on("data", (x) => {
process.stdout.write(x.toString());
});
p.stderr.on("data", (x) => {
process.stderr.write(x.toString());
});
}
cmd("bash", "-c", "echo one; sleep 1; echo two; sleep 1");
console.log("This must happen last.");
The solution is to wrap it in a promise and use await
. (If you're using .mjs
you can use await
without being in an async function.)
const { spawn } = require("child_process");
const process = require("process");
function cmd(...command) {
let p = spawn(command[0], command.slice(1));
return new Promise((resolveFunc) => {
p.stdout.on("data", (x) => {
process.stdout.write(x.toString());
});
p.stderr.on("data", (x) => {
process.stderr.write(x.toString());
});
p.on("exit", (code) => {
resolveFunc(code);
});
});
}
async function main() {
await cmd("bash", "-c", "echo one; sleep 1; echo two; sleep 1");
console.log("This must happen last.");
}
main();