217

I need in node.js function

result = execSync('node -v');

that will synchronously execute the given command line and return all stdout'ed by that command text.

ps. Sync is wrong. I know. Just for personal use.

UPDATE

Now we have mgutz's solution which gives us exit code, but not stdout! Still waiting for a more precise answer.

UPDATE

mgutz updated his answer and the solution is here :)
Also, as dgo.a mentioned, there is stand-alone module exec-sync

UPDATE 2014-07-30

ShellJS lib arrived. Consider this is the best choice for now.


UPDATE 2015-02-10

AT LAST! NodeJS 0.12 supports execSync natively.
See official docs

disfated
  • 10,633
  • 12
  • 39
  • 50
  • 35
    don't let yourself get fooled, sync is not wrong... EVEN in NodeJS all of your code is executed synchronously unless you explicitly call an async method ... if *everything* was done the asynchronous way *nothing* would ever be done. also, preferring asynchronous methods doesn't mean your lengthy calculation won't block your server. it's a choice. that the makers of Node chose to provide synchronous file system methods alongside the async ones just goes to show that there's a place for those, too. – flow Nov 16 '13 at 16:01
  • 2
    Where can we find the "Unix shell emulation library" you are talking about? – Florian Aug 09 '14 at 14:52
  • @Florian he means [ShellJS](https://github.com/arturadib/shelljs) – xst Jun 28 '15 at 18:39

14 Answers14

222

Node.js (since version 0.12 - so for a while) supports execSync:

child_process.execSync(command[, options])

You can now directly do this:

const execSync = require('child_process').execSync;
code = execSync('node -v');

and it'll do what you expect. (Defaults to pipe the i/o results to the parent process). Note that you can also spawnSync now.

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
54

See execSync library.

It's fairly easy to do with node-ffi. I wouldn't recommend for server processes, but for general development utilities it gets things done. Install the library.

npm install node-ffi

Example script:

var FFI = require("node-ffi");
var libc = new FFI.Library(null, {
  "system": ["int32", ["string"]]
});

var run = libc.system;
run("echo $USER");

[EDIT Jun 2012: How to get STDOUT]

var lib = ffi.Library(null, {
    // FILE* popen(char* cmd, char* mode);
    popen: ['pointer', ['string', 'string']],

    // void pclose(FILE* fp);
    pclose: ['void', [ 'pointer']],

    // char* fgets(char* buff, int buff, in)
    fgets: ['string', ['string', 'int','pointer']]
});

function execSync(cmd) {
  var
    buffer = new Buffer(1024),
    result = "",
    fp = lib.popen(cmd, 'r');

  if (!fp) throw new Error('execSync error: '+cmd);

  while(lib.fgets(buffer, 1024, fp)) {
    result += buffer.readCString();
  };
  lib.pclose(fp);

  return result;
}

console.log(execSync('echo $HOME'));
mgutz
  • 933
  • 1
  • 8
  • 7
  • 2
    How would you go about actually getting anything sent to `stdout` from this? All I can get is the process exit code –  Aug 24 '11 at 22:55
  • @cwolves: I think async would be better this. ([Ivo's answer](http://stackoverflow.com/a/4443678/432354)) – pvorb Dec 28 '11 at 14:45
  • @pvorb -- yeah, except when you can't use async :) –  Dec 28 '11 at 16:05
  • @cwolves - see my answer below for getting the stdout results in a synchronous manner, you don't need node-ffi, but you can use the "pipe stdout and stderr to a file path" mechanism with node-ffi and readfilesync the results. My solution however does not return the exit code, not sure if that's possible with command line options or what not, but I bet there's a shell utility that would report the exit code of a sub process to stdout if you needed that and don't want to use node-ffi. – Marcus Pope Jan 29 '12 at 07:58
  • 1
    There are valid reasons for not using the async hammer for every nail. For example, template engines are async in Express 3 and helper functions (locals) need to be synchronous. What if those helper functions need to compile Less files asynchronously on the fly? – mgutz Jun 09 '12 at 20:57
  • To complete my thought, I use the `execSync` trick while in **DEVELOPMENT** mode to precompile cache-busting assets with MD5 sums. – mgutz Jun 09 '12 at 21:09
  • 8
    I wonder why this simple `execSync` is not part of `child_process`. I think, it should be. – Michael Härtl Sep 05 '12 at 10:23
32

Use ShellJS module.

exec function without providing callback.

Example:

var version = exec('node -v').output;
disfated
  • 10,633
  • 12
  • 39
  • 50
falko
  • 1,407
  • 14
  • 15
24

There's an excellent module for flow control in node.js called asyncblock. If wrapping the code in a function is OK for your case, the following sample may be considered:

var asyncblock = require('asyncblock');
var exec = require('child_process').exec;

asyncblock(function (flow) {
    exec('node -v', flow.add());
    result = flow.wait();
    console.log(result);    // There'll be trailing \n in the output

    // Some other jobs
    console.log('More results like if it were sync...');
});
nab
  • 4,751
  • 4
  • 31
  • 42
  • 2
    He asked explicitly about sync version, not control flow libraries. – Alex Craft Oct 19 '13 at 17:50
  • 25
    @AlexeyPetrushin Every question here is about a goal, not about particular way to achieve it. Thanks for downvoting though. – nab Oct 20 '13 at 10:57
  • 1
    Also, this is a very useful answer for Windows users; installing `exec-sync` or `ffi` on Windows has a huge overhead (VC++, SDKs, Python, etc), but this is lighter. – Mendhak Jan 02 '14 at 12:56
16

Native Node.js solution is:

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

const result = execSync('node -v'); //  this do the trick 

Just be aware that some commands returns Buffer instead of string. And if you need string just add encoding to execSync options:

const result = execSync('git rev-parse HEAD', {encoding: 'utf8'});

... and it is also good to have timeout on sync exec:

const result = execSync('git rev-parse HEAD', {encoding: 'utf8', timeout: 10000});
ogram
  • 161
  • 1
  • 4
9

This is the easiest way I found:

exec-Sync: https://github.com/jeremyfa/node-exec-sync
(Not to be confused with execSync.)
Execute shell command synchronously. Use this for migration scripts, cli programs, but not for regular server code.

Example:

var execSync = require('exec-sync');   
var user = execSync('echo $USER');
console.log(user);
dgo.a
  • 2,634
  • 23
  • 35
9

Just to add that even though there are few usecases where you should use them, spawnSync / execFileSync / execSync were added to node.js in these commits: https://github.com/joyent/node/compare/d58c206862dc...e8df2676748e

kvz
  • 5,517
  • 1
  • 42
  • 33
9

This is not possible in Node.js, both child_process.spawn and child_process.exec were built from the ground up to be async.

For details see: https://github.com/ry/node/blob/master/lib/child_process.js

If you really want to have this blocking, then put everything that needs to happen afterwards in a callback, or build your own queue to handle this in a blocking fashion, I suppose you could use Async.js for this task.

Or, in case you have way too much time to spend, hack around in Node.js it self.

Ivo Wetzel
  • 46,459
  • 16
  • 98
  • 112
  • 12
    strange because the file system module has synchronous calls. Why not also execute? – Alfred Dec 14 '10 at 23:45
  • 4
    @Alfred The sync FS calls are mainly in there for loading of configs at program start. – Ivo Wetzel Dec 15 '10 at 06:46
  • 3
    @IvoWetzel - tisk tisk... haven't we learned to never say something is impossible? ;) see my solution below. – Marcus Pope Jan 29 '12 at 07:49
  • 1
    @IvoWetzel "The sync FS calls..."—right, and sometimes you want to, say, issue a command to compile something at program start and continue on completion.—given that there are sync FS calls, not having a sync exec does look like an oversight. i'm all for asynchronous, but synchronous does have its pros and use cases. gotta use it in a judicious way, of course. – flow Nov 16 '13 at 16:31
  • Async is fine, but if WidgetB depends on the final results of WidgetA, all the async in the world won't get the job done. Sometimes processes have to be synchronous. Try cooking asynchronously. ;) – Lloyd Sargent Jan 08 '14 at 01:05
6

my way since 5 years is to have 2 lines ;

const { execSync } = require('child_process');
const shell = (cmd) => execSync(cmd, {encoding: 'utf8'});

Then enjoy: shell('git remote -v') or out = shell('ls -l') .. so on

Abdennour TOUMI
  • 87,526
  • 38
  • 249
  • 254
5

You can achieve this using fibers. For example, using my Common Node library, the code would look like this:

result = require('subprocess').command('node -v');
Oleg
  • 613
  • 7
  • 6
3

I had a similar problem and I ended up writing a node extension for this. You can check out the git repository. It's open source and free and all that good stuff !

https://github.com/aponxi/npm-execxi

ExecXI is a node extension written in C++ to execute shell commands one by one, outputting the command's output to the console in real-time. Optional chained, and unchained ways are present; meaning that you can choose to stop the script after a command fails (chained), or you can continue as if nothing has happened !

Usage instructions are in the ReadMe file. Feel free to make pull requests or submit issues!

EDIT: However it doesn't return the stdout yet... Just outputs them in real-time. It does now. Well, I just released it today. Maybe we can build on it.

Anyway, I thought it was worth to mention it.

Logan
  • 10,649
  • 13
  • 41
  • 54
  • This is exactly what I have been looking for. Thank you for putting in the time to make this. – thealfreds Apr 16 '13 at 22:20
  • The only thing I haven't figured out and implemented is the build configuration for different nodejs versions. I guess I wrote it on node 0.8 (https://travis-ci.org/aponxi/npm-execxi/builds/5248535) so as long as `npm install` succeeds (in other words as plugin compiles), then it is good to go for production. Besides targeting different nodejs versions, I'd say it's patched up for production, or that it's pretty stable. If there are any bugs you can send in a pull request or post an issue at github :) – Logan Nov 03 '13 at 04:09
3

I get used to implement "synchronous" stuff at the end of the callback function. Not very nice, but it works. If you need to implement a sequence of command line executions you need to wrap exec into some named function and recursively call it. This pattern seem to be usable for me:

SeqOfExec(someParam);

function SeqOfExec(somepParam) {
    // some stuff
    // .....
    // .....

    var execStr = "yourExecString";
    child_proc.exec(execStr, function (error, stdout, stderr) {
        if (error != null) {
            if (stdout) {
                throw Error("Smth goes wrong" + error);
            } else {
                // consider that empty stdout causes
                // creation of error object
            }
        }
        // some stuff
        // .....
        // .....

        // you also need some flag which will signal that you 
        // need to end loop
        if (someFlag ) {
            // your synch stuff after all execs
            // here
            // .....
        } else {
            SeqOfExec(someAnotherParam);
        }
    });
};
scherka
  • 81
  • 1
  • 10
1

I actually had a situation where I needed to run multiple commands one after another from a package.json preinstall script in a way that would work on both Windows and Linux/OSX, so I couldn't rely on a non-core module.

So this is what I came up with:

#cmds.coffee
childproc = require 'child_process'

exports.exec = (cmds) ->
  next = ->
    if cmds.length > 0
      cmd = cmds.shift()
      console.log "Running command: #{cmd}"
      childproc.exec cmd, (err, stdout, stderr) ->
        if err? then console.log err
        if stdout? then console.log stdout
        if stderr? then console.log stderr
        next()
    else
      console.log "Done executing commands."

  console.log "Running the follows commands:"
  console.log cmds
  next()

You can use it like this:

require('./cmds').exec ['grunt coffee', 'nodeunit test/tls-config.js']

EDIT: as pointed out, this doesn't actually return the output or allow you to use the result of the commands in a Node program. One other idea for that is to use LiveScript backcalls. http://livescript.net/

Jason Livesay
  • 6,317
  • 3
  • 25
  • 31
  • Thanks, but this is not an answer since your code runs commands in series **asynchronously** – disfated May 21 '13 at 02:32
  • If you need to execute a series of commands synchronously as per my example it will work. I think I misunderstood your question though, sorry. I added another idea to the answer. – Jason Livesay May 21 '13 at 03:03
1

you can do synchronous shell operations in nodejs like so:

var execSync = function(cmd) {

    var exec  = require('child_process').exec;
    var fs = require('fs');

    //for linux use ; instead of &&
    //execute your command followed by a simple echo 
    //to file to indicate process is finished
    exec(cmd + " > c:\\stdout.txt && echo done > c:\\sync.txt");

    while (true) {
        //consider a timeout option to prevent infinite loop
        //NOTE: this will max out your cpu too!
        try {
            var status = fs.readFileSync('c:\\sync.txt', 'utf8');

            if (status.trim() == "done") {
                var res = fs.readFileSync("c:\\stdout.txt", 'utf8');
                fs.unlinkSync("c:\\stdout.txt"); //cleanup temp files
                fs.unlinkSync("c:\\sync.txt");
                return res;
            }
        } catch(e) { } //readFileSync will fail until file exists
    }

};

//won't return anything, but will take 10 seconds to run
console.log(execSync("sleep 10")); 

//assuming there are a lot of files and subdirectories, 
//this too may take a while, use your own applicable file path
console.log(execSync("dir /s c:\\usr\\docs\\"));

EDIT - this example is meant for windows environments, adjust for your own linux needs if necessary

Marcus Pope
  • 2,293
  • 20
  • 25
  • Yes, and as fast as your CPU can possibly summon... But hey when you "need" to do something evil, Satan's your man right? – Marcus Pope Feb 01 '12 at 22:06
  • ok, this is pure evil, but awesome. I needed this to handle a filesystem event browserify.on('register'), that did not have a callback. Saved my day! – Robert Gould Mar 27 '12 at 10:35
  • can you explain why this works on javascript engines? shouldn't the while loop be executed infinitely on the same tick while exec executes on the next one? – badunk Feb 20 '13 at 20:03
  • Maxing out the CPU by busy waiting is bad design. – Louis Oct 31 '13 at 12:27
  • @Louis-DominiqueDubeau Sure, but there isn't really any alternative that doesn't depend on some third party source that may or may not be cross platform compatible. It's also not a true maxing out of the CPU because the OS won't give full priority to the nodejs process. I think sync implementations of shell ops are on the horizon or perhaps here already. – Marcus Pope Oct 31 '13 at 20:33
  • @badunk - IIRC, exec spawns the process immediately, and the while loop would run in the next tick until the OS created the temp file. Haven't had to use this logic since so I'm not sure if internals have changed :/ – Marcus Pope Oct 31 '13 at 20:35