8

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) }
});
1

3 Answers 3

12

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}"`);

Sign up to request clarification or add additional context in comments.

Comments

2

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.

(Note that you can also use spawn(cmd, { shell: true }), without the empty array as second argument, as per the comment below).

1 Comment

You can avoid second argument. This also works spawn(cmd, { shell: true })
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);

1 Comment

This is not a good solution, what do you do with arguments of two words with a space --key="some value"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.