7

I'm working on spawn on nodejs. previously I was using exec but exec has no compatibility in stdout (or stderr) steaming. now, I have a disability about spawn. It seems whereas exec accepted quoted string command, spawn does not accepted that one, just array format.

so following exec script is working correctly but another one which uses spawn will be error, due to string formatted command not array.

const exec = require('child_process').exec;

exec(` echo foo bar `, (error, stdout, stderr) => {
  if ( error ){ console.log(error) }
  if ( stdout ){ console.log(stdout) }
  if ( stderr ){ console.log(stderr) }
});
const { spawn } = require('child_process');

command = spawn('echo foo bar');

command.stdout.on('data', function (data) {
  console.log(data.toString());
});

command.stderr.on('data', function (data) {
  console.log( data.toString());
});

command.on('exit', function (code) {
  console.log( code.toString());
});

I have a lot of command line script which is what I want to spawn on nodejs, and all of them are complicated, something like following one. that's why I want to use string format for specify command rather than array. Any idea?

exec(' bash -ic "expecto \\"sudo bash -ic \\\\\\"rd ; backup_important_batch\\\\\\"\\" $PASSWORD" ', (error, stdout, stderr) => {
    if ( error ){ console.log(error) }
    if ( stdout ){ console.log(stdout) }
    if ( stderr ){ console.log(stderr) }
});
k23j4
  • 723
  • 2
  • 8
  • 15
  • You can use the `{shell: true}` option, as seen here: https://stackoverflow.com/a/45134890/2441655 – Venryx Oct 29 '20 at 12:30

3 Answers3

10

You can't split on spaces because many command line args may include strings like ./foo.js --bar "Hello Baz", splitting on the string will incorrectly give you "Hello Baz" as arguments.

Use a library like https://www.npmjs.com/package/string-argv to convert the string into arguments that you can pass the string to, get an array result and build your spawn() call.

Edit: Here's an example:

const { spawn } = require('child_process');
const { parseArgsStringToArgv } = require('string-argv');

async function run(command) {
    return new Promise( resolve => {
        let args = parseArgsStringToArgv(command);
        let cmd = args.shift();

        console.log(cmd, args);

        let step = spawn(cmd, args);

        step.stdout.pipe(process.stdout);
        step.stderr.pipe(process.stderr);

        step.on('close', (code) => {
           resolve(code);
        });
    });
}

let name = 'Baz';
let exitCode = await run(`echo "Hello ${name}"`);
Jay Proulx
  • 101
  • 4
1

try auto splitting the command like:

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

let cmd = 'echo foo bar'
let cmdarray = cmd.split(" ");
let command = spawn(cmdarray.shift(), cmdarray);
  • This is not a good solution, what do you do with arguments of two words with a space `--key="some value"` – ABE Sep 22 '22 at 21:11
0

As per Venryx's comment above, which links the existing answer at https://stackoverflow.com/a/45134890/2441655 , you can use spawn(cmd, [], { shell: true })

e.g.

const { spawn } = require('child_process');
const command = spawn('echo foo bar', [], { shell: true });
// alternative, as per comment by ZuzEL:
// spawn('echo foo bar', { shell: true }) 

command.stdout.on('data',data => console.log(data.toString()));
command.stderr.on('data', function (data) {console.log( data.toString());});
command.on('exit', function (code) {console.log( code.toString());});

// foo bar
//
// 0

See Node.js docs for spawn here.

phhu
  • 1,462
  • 13
  • 33