823

I am in the process of porting a CLI library from Ruby over to Node.js. In my code I execute several third party binaries when necessary. I am not sure how best to accomplish this in Node.

Here's an example in Ruby where I call PrinceXML to convert a file to a PDF:

cmd = system("prince -v builds/pdf/book.html -o builds/pdf/book.pdf")

What is the equivalent code in Node?

hexacyanide
  • 88,222
  • 31
  • 159
  • 162
Dave Thompson
  • 8,453
  • 3
  • 16
  • 11
  • 3
    [This](https://github.com/IndigoUnited/node-cross-spawn) library is a good place to start. It allows you to spawn processes across all os platforms. – mattyb Jun 11 '17 at 05:59
  • 2
    Possible duplicate of [Execute and get the output of a shell command in node.js](https://stackoverflow.com/questions/12941083/execute-and-get-the-output-of-a-shell-command-in-node-js) – Damjan Pavlica Jul 17 '18 at 12:57
  • 3
    Simplest is to use child_process.exec, here are some [good examples](https://www.codota.com/code/javascript/functions/child_process/exec) – drorw Apr 18 '19 at 20:39

12 Answers12

1292

For even newer version of Node.js (v8.1.4), the events and calls are similar or identical to older versions, but it's encouraged to use the standard newer language features. Examples:

For buffered, non-stream formatted output (you get it all at once), use child_process.exec:

const { exec } = require('child_process');
exec('cat *.js bad_file | wc -l', (err, stdout, stderr) => {
  if (err) {
    // node couldn't execute the command
    return;
  }

  // the *entire* stdout and stderr (buffered)
  console.log(`stdout: ${stdout}`);
  console.log(`stderr: ${stderr}`);
});

You can also use it with Promises:

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

async function ls() {
  const { stdout, stderr } = await exec('ls');
  console.log('stdout:', stdout);
  console.log('stderr:', stderr);
}
ls();

If you wish to receive the data gradually in chunks (output as a stream), use child_process.spawn:

const { spawn } = require('child_process');
const child = spawn('ls', ['-lh', '/usr']);

// use child.stdout.setEncoding('utf8'); if you want text chunks
child.stdout.on('data', (chunk) => {
  // data from standard output is here as buffers
});

// since these are streams, you can pipe them elsewhere
child.stderr.pipe(dest);

child.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});

Both of these functions have a synchronous counterpart. An example for child_process.execSync:

const { execSync } = require('child_process');
// stderr is sent to stderr of parent process
// you can set options.stdio if you want it to go elsewhere
let stdout = execSync('ls');

As well as child_process.spawnSync:

const { spawnSync} = require('child_process');
const child = spawnSync('ls', ['-lh', '/usr']);

console.log('error', child.error);
console.log('stdout ', child.stdout);
console.log('stderr ', child.stderr);

Note: The following code is still functional, but is primarily targeted at users of ES5 and before.

The module for spawning child processes with Node.js is well documented in the documentation (v5.0.0). To execute a command and fetch its complete output as a buffer, use child_process.exec:

var exec = require('child_process').exec;
var cmd = 'prince -v builds/pdf/book.html -o builds/pdf/book.pdf';

exec(cmd, function(error, stdout, stderr) {
  // command output is in stdout
});

If you need to use handle process I/O with streams, such as when you are expecting large amounts of output, use child_process.spawn:

var spawn = require('child_process').spawn;
var child = spawn('prince', [
  '-v', 'builds/pdf/book.html',
  '-o', 'builds/pdf/book.pdf'
]);

child.stdout.on('data', function(chunk) {
  // output will be here in chunks
});

// or if you want to send output elsewhere
child.stdout.pipe(dest);

If you are executing a file rather than a command, you might want to use child_process.execFile, which parameters which are almost identical to spawn, but has a fourth callback parameter like exec for retrieving output buffers. That might look a bit like this:

var execFile = require('child_process').execFile;
execFile(file, args, options, function(error, stdout, stderr) {
  // command output is in stdout
});

As of v0.11.12, Node now supports synchronous spawn and exec. All of the methods described above are asynchronous, and have a synchronous counterpart. Documentation for them can be found here. While they are useful for scripting, do note that unlike the methods used to spawn child processes asynchronously, the synchronous methods do not return an instance of ChildProcess.

Tomoyuki Aota
  • 867
  • 9
  • 18
hexacyanide
  • 88,222
  • 31
  • 159
  • 162
  • 30
    THANK YOU. This was driving me nuts. Sometimes it helps to just have the obvious solution pointed out so we noobs (to node) can learn and run with it. – Dave Thompson Dec 19 '13 at 00:42
  • 16
    Note : require('child_process').execFile() will be of interest for people who need to run a file rather than a system-wide known command like prince here. – Louis Ameline Feb 16 '14 at 16:20
  • 3
    Instead of `child.pipe(dest)` (which doesn't exist), you have to use `child.stdout.pipe(dest)` and `child.stderr.pipe(dest)`, e.g. `child.stdout.pipe(process.stdout)` and `child.stderr.pipe(process.stderr)`. – ComFreek Aug 28 '15 at 13:29
  • What if I don't want to put everything into a file, but I want to execute more than one command? Maybe like `echo "hello"` and `echo "world"`. – Cameron May 24 '17 at 15:24
  • is this the standard way to do this ? i mean how all the wrapper are written in nodejs ? i mean let's say for gearman,rabbitmq etc. which require to run the command but they also have some wrapper as well but i can't find any of this code in their library code – Rishabh Agrawal Oct 14 '17 at 16:24
  • @hexacyanide . any possible to open `c:windows\system32\osk.exe` form child process options. – Mathankumar K Feb 07 '18 at 19:08
  • Note that if you want all of the child's input and output to just be passed straight through, then you can call spawn like this: `spawn('ls', { stdio: 'inherit' })`. That way you don't have to stuff around with `child.stdout.on('data'...` and all the rest of it. – Cam Jackson Dec 09 '21 at 13:09
320

Node JS v15.8.0, LTS v14.15.4, and v12.20.1 --- Feb 2021

Async method (Unix):

'use strict';

const { spawn } = require( 'child_process' );
const ls = spawn( 'ls', [ '-lh', '/usr' ] );

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

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

ls.on( 'close', ( code ) => {
    console.log( `child process exited with code ${ code }` );
} );

Async method (Windows):

'use strict';

const { spawn } = require( 'child_process' );
// NOTE: Windows Users, this command appears to be differ for a few users.
// You can think of this as using Node to execute things in your Command Prompt.
// If `cmd` works there, it should work here.
// If you have an issue, try `dir`:
// const dir = spawn( 'dir', [ '.' ] );
const dir = spawn( 'cmd', [ '/c', 'dir' ] );

dir.stdout.on( 'data', ( data ) => console.log( `stdout: ${ data }` ) );
dir.stderr.on( 'data', ( data ) => console.log( `stderr: ${ data }` ) );
dir.on( 'close', ( code ) => console.log( `child process exited with code ${code}` ) );

Sync:

'use strict';

const { spawnSync } = require( 'child_process' );
const ls = spawnSync( 'ls', [ '-lh', '/usr' ] );

console.log( `stderr: ${ ls.stderr.toString() }` );
console.log( `stdout: ${ ls.stdout.toString() }` );

From Node.js v15.8.0 Documentation

The same goes for Node.js v14.15.4 Documentation and Node.js v12.20.1 Documentation

iSkore
  • 7,394
  • 3
  • 34
  • 59
  • 12
    Thank you for giving both proper and simple versions. The slightly simpler sync version was totally fine for my one off "do something and throw it away" script that I needed. – Brian Jorden Apr 15 '17 at 13:27
  • 1
    No problem! Always nice to have both even if it's not "proper" according to some. – iSkore Apr 15 '17 at 13:30
  • 9
    Might be worth pointing out that in order to do this example in Windows, one has to use `'cmd', ['/c', 'dir']`. At least I was just searching high and low why `'dir'` without arguments doesn't work before I remembered this... ;) – AndyO Apr 13 '18 at 21:31
  • 1
    None of these output ANYTHING to the console. – Tyguy7 Sep 15 '18 at 00:24
  • @Tyguy7 how are you running it? And do you have any overrides on the console object? – iSkore Sep 15 '18 at 03:02
  • Why do you claim one method is proper? – Stephen Sep 30 '18 at 12:49
  • I’m assuming you are reffering to why async is proper? Because of how the event loop works with synchronous functions. Synchronous functions will stop all other executions until the synchronous function is complete. For instance, let’s say you’re running a server that accepts HTTP requests that dispatch a synchronous CLI command. Let’s say an ffmpeg command that could take minutes or more. All other requests would be completely blocked until that original request is fulfilled. All recommendations in the node spec recommend using async, for that reason I specify “proper” – iSkore Oct 01 '18 at 02:54
  • But I added the synchronous method example because its still valid and in testing senarios or during a programs “initialization” stage, when the program first starts, and never runs those commands again, synchronous methods are a valid use case. But during standard runtime of a program, synchronous methods are not advised. – iSkore Oct 01 '18 at 02:57
  • @iSkore I am executing a command using ('child_process').exec but it gives me an error Error: Command failed: unoconv -f pdf "./document.docx" 'unoconv' is not recognized as an internal or external command, operable program or batch file. This command is working when I run it in git commandline tool. – Nouman Dilshad Dec 19 '18 at 10:14
  • Sounds like you are running on a windows machine (if you have a “git CLI tool) and the unoconv tool isn’t installed. Try running that unoconv command in your powershell. If it doesn’t run there, it won’t run in node. This answer is the “general use case” so try running the `dir` or `ls` command. If that doesn’t work the function is setup incorrectly. – iSkore Dec 19 '18 at 11:39
  • May I suggest adding a short explanation to the top of this clarifying that `async` is what's meant by proper? In the context of the other responses, my first understanding was conversely that `spawn` was _more_ proper than the other alternatives, such as `exec`. Especially since you say "From the documentation". This gives the impression that the documentation advises using `spawn` over alternatives, when the documentation actually highlights that `spawn` is the underlying mechanism for all the others. – Lindsay Ryan May 18 '19 at 08:24
  • super silly question: what is the meaning of declaring const { spawnSync } (what is the meaning of the curly braces around 'spawnSync ' ?) – OhadR Mar 23 '20 at 22:17
  • Not a silly question at all! [here’s a resource for you](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) - object destructuring is a way to extract a key from an object. Like this: `const { a, b } = { a: 10, b: 20 }` is the same as `const a = obj.a, b = obj.b;` – iSkore Mar 24 '20 at 03:37
  • It only imports what you want opposed to a reference to the entire module – iSkore Mar 24 '20 at 03:45
86

You are looking for child_process.exec

Here is the example:

const exec = require('child_process').exec;
const child = exec('cat *.js bad_file | wc -l',
    (error, stdout, stderr) => {
        console.log(`stdout: ${stdout}`);
        console.log(`stderr: ${stderr}`);
        if (error !== null) {
            console.log(`exec error: ${error}`);
        }
});
iSkore
  • 7,394
  • 3
  • 34
  • 59
Andrzej Karpuszonak
  • 8,896
  • 2
  • 38
  • 50
  • This is correct. But be aware that this kind of calling a child-process has limitations for the length of stdout. – hgoebl Dec 17 '13 at 20:03
  • @hgoebl, what is the alternative then? – Harshdeep Oct 09 '15 at 07:00
  • 2
    @Harshdeep in case of long stdout outputs (several MB e.g.) you can listen to `data` events on stdout. Look in the docs, but it must be something like `childProc.stdout.on("data", fn)`. – hgoebl Oct 09 '15 at 12:32
50

Since version 4 the closest alternative is child_process.execSync method:

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

let output = execSync('prince -v builds/pdf/book.html -o builds/pdf/book.pdf');

⚠️ Note that execSync call blocks event loop.

Paul Rumkin
  • 6,737
  • 2
  • 25
  • 35
  • This works great on latest node. Is a `child_process` being created when using `execSync` though? And does it get removed right after the command, right? So no memory leaks? – NiCk Newman Jun 14 '16 at 10:02
  • 2
    Yes, there is no memory leaks. I guess it initialises only libuv child process structures without creating it in node at all. – Paul Rumkin Jun 14 '16 at 13:24
36

Now you can use shelljs (from node v4) as follows:

var shell = require('shelljs');

shell.echo('hello world');
shell.exec('node --version');

Install with

npm install shelljs

See https://github.com/shelljs/shelljs

Brian Burns
  • 20,575
  • 8
  • 83
  • 77
FacePalm
  • 10,992
  • 5
  • 48
  • 50
34

If you want something that closely resembles the top answer but is also synchronous then this will work.

var execSync = require('child_process').execSync;
var cmd = "echo 'hello world'";

var options = {
  encoding: 'utf8'
};

console.log(execSync(cmd, options));
Vlad
  • 7,997
  • 3
  • 56
  • 43
Cameron
  • 2,805
  • 3
  • 31
  • 45
33
const exec = require("child_process").exec
exec("ls", (error, stdout, stderr) => {
 //do whatever here
})
Ben Bieler
  • 1,518
  • 1
  • 14
  • 22
  • 18
    Please add more explanation for how this code works and how it solves the answer. Remember that StackOverflow is building an archive of answers for people reading this in the future. – Al Sweigart Apr 15 '17 at 18:08
  • 6
    What Al said is true, but I will say the benefit of this answer is that it is so much simpler than having to read through the top answer for someone who needs a quick response. –  Sep 25 '18 at 22:18
17

I just wrote a Cli helper to deal with Unix/windows easily.

Javascript:

define(["require", "exports"], function (require, exports) {
    /**
     * Helper to use the Command Line Interface (CLI) easily with both Windows and Unix environments.
     * Requires underscore or lodash as global through "_".
     */
    var Cli = (function () {
        function Cli() {}
            /**
             * Execute a CLI command.
             * Manage Windows and Unix environment and try to execute the command on both env if fails.
             * Order: Windows -> Unix.
             *
             * @param command                   Command to execute. ('grunt')
             * @param args                      Args of the command. ('watch')
             * @param callback                  Success.
             * @param callbackErrorWindows      Failure on Windows env.
             * @param callbackErrorUnix         Failure on Unix env.
             */
        Cli.execute = function (command, args, callback, callbackErrorWindows, callbackErrorUnix) {
            if (typeof args === "undefined") {
                args = [];
            }
            Cli.windows(command, args, callback, function () {
                callbackErrorWindows();

                try {
                    Cli.unix(command, args, callback, callbackErrorUnix);
                } catch (e) {
                    console.log('------------- Failed to perform the command: "' + command + '" on all environments. -------------');
                }
            });
        };

        /**
         * Execute a command on Windows environment.
         *
         * @param command       Command to execute. ('grunt')
         * @param args          Args of the command. ('watch')
         * @param callback      Success callback.
         * @param callbackError Failure callback.
         */
        Cli.windows = function (command, args, callback, callbackError) {
            if (typeof args === "undefined") {
                args = [];
            }
            try {
                Cli._execute(process.env.comspec, _.union(['/c', command], args));
                callback(command, args, 'Windows');
            } catch (e) {
                callbackError(command, args, 'Windows');
            }
        };

        /**
         * Execute a command on Unix environment.
         *
         * @param command       Command to execute. ('grunt')
         * @param args          Args of the command. ('watch')
         * @param callback      Success callback.
         * @param callbackError Failure callback.
         */
        Cli.unix = function (command, args, callback, callbackError) {
            if (typeof args === "undefined") {
                args = [];
            }
            try {
                Cli._execute(command, args);
                callback(command, args, 'Unix');
            } catch (e) {
                callbackError(command, args, 'Unix');
            }
        };

        /**
         * Execute a command no matters what's the environment.
         *
         * @param command   Command to execute. ('grunt')
         * @param args      Args of the command. ('watch')
         * @private
         */
        Cli._execute = function (command, args) {
            var spawn = require('child_process').spawn;
            var childProcess = spawn(command, args);

            childProcess.stdout.on("data", function (data) {
                console.log(data.toString());
            });

            childProcess.stderr.on("data", function (data) {
                console.error(data.toString());
            });
        };
        return Cli;
    })();
    exports.Cli = Cli;
});

Typescript original source file:

 /**
 * Helper to use the Command Line Interface (CLI) easily with both Windows and Unix environments.
 * Requires underscore or lodash as global through "_".
 */
export class Cli {

    /**
     * Execute a CLI command.
     * Manage Windows and Unix environment and try to execute the command on both env if fails.
     * Order: Windows -> Unix.
     *
     * @param command                   Command to execute. ('grunt')
     * @param args                      Args of the command. ('watch')
     * @param callback                  Success.
     * @param callbackErrorWindows      Failure on Windows env.
     * @param callbackErrorUnix         Failure on Unix env.
     */
    public static execute(command: string, args: string[] = [], callback ? : any, callbackErrorWindows ? : any, callbackErrorUnix ? : any) {
        Cli.windows(command, args, callback, function () {
            callbackErrorWindows();

            try {
                Cli.unix(command, args, callback, callbackErrorUnix);
            } catch (e) {
                console.log('------------- Failed to perform the command: "' + command + '" on all environments. -------------');
            }
        });
    }

    /**
     * Execute a command on Windows environment.
     *
     * @param command       Command to execute. ('grunt')
     * @param args          Args of the command. ('watch')
     * @param callback      Success callback.
     * @param callbackError Failure callback.
     */
    public static windows(command: string, args: string[] = [], callback ? : any, callbackError ? : any) {
        try {
            Cli._execute(process.env.comspec, _.union(['/c', command], args));
            callback(command, args, 'Windows');
        } catch (e) {
            callbackError(command, args, 'Windows');
        }
    }

    /**
     * Execute a command on Unix environment.
     *
     * @param command       Command to execute. ('grunt')
     * @param args          Args of the command. ('watch')
     * @param callback      Success callback.
     * @param callbackError Failure callback.
     */
    public static unix(command: string, args: string[] = [], callback ? : any, callbackError ? : any) {
        try {
            Cli._execute(command, args);
            callback(command, args, 'Unix');
        } catch (e) {
            callbackError(command, args, 'Unix');
        }
    }

    /**
     * Execute a command no matters what's the environment.
     *
     * @param command   Command to execute. ('grunt')
     * @param args      Args of the command. ('watch')
     * @private
     */
    private static _execute(command, args) {
        var spawn = require('child_process').spawn;
        var childProcess = spawn(command, args);

        childProcess.stdout.on("data", function (data) {
            console.log(data.toString());
        });

        childProcess.stderr.on("data", function (data) {
            console.error(data.toString());
        });
    }
}

Example of use:

    Cli.execute(Grunt._command, args, function (command, args, env) {
        console.log('Grunt has been automatically executed. (' + env + ')');

    }, function (command, args, env) {
        console.error('------------- Windows "' + command + '" command failed, trying Unix... ---------------');

    }, function (command, args, env) {
        console.error('------------- Unix "' + command + '" command failed too. ---------------');
    });
Mosho
  • 7,099
  • 3
  • 34
  • 51
Vadorequest
  • 16,593
  • 24
  • 118
  • 215
  • 1
    Most recent version there, with usage example to use Grunt in CLI: https://gist.github.com/Vadorequest/f72fa1c152ec55357839 – Vadorequest Mar 23 '15 at 13:18
13

Use this lightweight npm package: system-commands

Look at it here.

Import it like this:

const system = require('system-commands')

Run commands like this:

system('ls').then(output => {
    console.log(output)
}).catch(error => {
    console.error(error)
})
Ken Mueller
  • 3,659
  • 3
  • 21
  • 33
11

If you don't mind a dependency and want to use promises, child-process-promise works:

installation

npm install child-process-promise --save

exec Usage

var exec = require('child-process-promise').exec;
 
exec('echo hello')
    .then(function (result) {
        var stdout = result.stdout;
        var stderr = result.stderr;
        console.log('stdout: ', stdout);
        console.log('stderr: ', stderr);
    })
    .catch(function (err) {
        console.error('ERROR: ', err);
    });

spawn usage

var spawn = require('child-process-promise').spawn;
 
var promise = spawn('echo', ['hello']);
 
var childProcess = promise.childProcess;
 
console.log('[spawn] childProcess.pid: ', childProcess.pid);
childProcess.stdout.on('data', function (data) {
    console.log('[spawn] stdout: ', data.toString());
});
childProcess.stderr.on('data', function (data) {
    console.log('[spawn] stderr: ', data.toString());
});
 
promise.then(function () {
        console.log('[spawn] done!');
    })
    .catch(function (err) {
        console.error('[spawn] ERROR: ', err);
    });

ECMAScript Modules import...from syntax

import {exec} from 'child-process-promise';
let result = await exec('echo hi');
console.log(result.stdout);
Jesus is Lord
  • 14,971
  • 11
  • 66
  • 97
4

@hexacyanide's answer is almost a complete one. On Windows command prince could be prince.exe, prince.cmd, prince.bat or just prince (I'm no aware of how gems are bundled, but npm bins come with a sh script and a batch script - npm and npm.cmd). If you want to write a portable script that would run on Unix and Windows, you have to spawn the right executable.

Here is a simple yet portable spawn function:

function spawn(cmd, args, opt) {
    var isWindows = /win/.test(process.platform);

    if ( isWindows ) {
        if ( !args ) args = [];
        args.unshift(cmd);
        args.unshift('/c');
        cmd = process.env.comspec;
    }

    return child_process.spawn(cmd, args, opt);
}

var cmd = spawn("prince", ["-v", "builds/pdf/book.html", "-o", "builds/pdf/book.pdf"])

// Use these props to get execution results:
// cmd.stdin;
// cmd.stdout;
// cmd.stderr;
DUzun
  • 1,693
  • 17
  • 15
0

Node 16:

const { execSync } = require('child_process');
execSync('ls'); // your system command
Richard
  • 2,396
  • 23
  • 23