node.js execute system command synchronously
Asked Answered
C

14

222

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

Catarinacatarrh answered 14/12, 2010 at 19:59 Comment(3)
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.Asp
Where can we find the "Unix shell emulation library" you are talking about?Ingar
@Ingar he means ShellJSRheotropism
A
227

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.

Allargando answered 8/2, 2015 at 14:13 Comment(5)
How can I disconnect from this subprocess?Glorygloryofthesnow
@Glorygloryofthesnow nodejs.org/api/…Basin
it returns <Buffer 76 31 31 2e 36 2e 30 0a> when i execSync('node -v'),what's that?Godric
That’s a buffer, you can toString itAllargando
also: if you have a cmd that takes a bit longer to execute, you probably want to use it with the option { stdio: 'inherit' } to get real time console output. execSync('yourCmd', { stdio: 'inherit' })Commonality
C
56

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'));
Chiastolite answered 9/6, 2011 at 5:23 Comment(7)
How would you go about actually getting anything sent to stdout from this? All I can get is the process exit codeFluvial
@cwolves: I think async would be better this. (Ivo's answer)Magdau
@Magdau -- yeah, except when you can't use async :)Fluvial
@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.Correy
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?Chiastolite
To complete my thought, I use the execSync trick while in DEVELOPMENT mode to precompile cache-busting assets with MD5 sums.Chiastolite
I wonder why this simple execSync is not part of child_process. I think, it should be.Pix
P
32

Use ShellJS module.

exec function without providing callback.

Example:

var version = exec('node -v').output;
Poche answered 29/7, 2014 at 12:58 Comment(3)
Note that at the time of writing, the docs mention that the synchronous exec() is CPU intensive for long processes.Merras
options, {silent:true} is keyRainout
This do something (other than provide shorter syntax) this doesn't? const execSync = require('child_process').execSync; code = execSync('node -v');Glindaglinka
R
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...');
});
Reproductive answered 27/2, 2012 at 13:15 Comment(3)
He asked explicitly about sync version, not control flow libraries.Paratuberculosis
@AlexeyPetrushin Every question here is about a goal, not about particular way to achieve it. Thanks for downvoting though.Reproductive
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.Likely
M
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});
Mise answered 31/8, 2020 at 10:18 Comment(0)
F
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);
Fulcrum answered 14/12, 2010 at 19:59 Comment(0)
B
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.

Boondoggle answered 14/12, 2010 at 20:10 Comment(5)
strange because the file system module has synchronous calls. Why not also execute?Choate
@Choate The sync FS calls are mainly in there for loading of configs at program start.Boondoggle
@IvoWetzel - tisk tisk... haven't we learned to never say something is impossible? ;) see my solution below.Correy
@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.Asp
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. ;)Castra
S
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

Spongy answered 12/5, 2014 at 18:21 Comment(2)
Does this mean we going to have it in v0.12 out of the box?Catarinacatarrh
@Catarinacatarrh yes: strongloop.com/strongblog/…Witcher
D
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

Dowland answered 14/8, 2021 at 22:26 Comment(0)
E
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');
Enceladus answered 7/4, 2012 at 19:35 Comment(0)
C
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);
        }
    });
};
Clichy answered 24/12, 2011 at 15:21 Comment(0)
M
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.

Mcdonough answered 28/2, 2013 at 7:25 Comment(2)
This is exactly what I have been looking for. Thank you for putting in the time to make this.Androgynous
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 (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 :)Mcdonough
C
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

Correy answered 29/1, 2012 at 7:45 Comment(6)
Yes, and as fast as your CPU can possibly summon... But hey when you "need" to do something evil, Satan's your man right?Correy
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!Zea
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?Piapiacenza
Maxing out the CPU by busy waiting is bad design.Ricoriki
@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.Correy
@Piapiacenza - 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 :/Correy
A
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/

Anciently answered 20/5, 2013 at 21:4 Comment(2)
Thanks, but this is not an answer since your code runs commands in series asynchronouslyCatarinacatarrh
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.Anciently

© 2022 - 2024 — McMap. All rights reserved.