node.js同步执行系统命令


171

我需要在node.js函数中

result = execSync('node -v');

它将同步执行给定的命令行并返回该命令文本的所有标准输出。

ps。同步是错误的。我知道。仅供个人使用。

更新

现在我们有了mgutz的解决方案,该解决方案为我们提供了退出代码,但没有stdout!仍在等待更精确的答案。

更新

mgutz更新了他的答案,解决方案在这里:)
另外,如dgo.a所述,有独立的模块exec-sync

更新2014-07-30

ShellJS库到达。考虑到这是目前的最佳选择。


更新2015-02-10

最后!NodeJS 0.12 execSync本机支持。
查看官方文件


26
不要让自己上当,同步是没有错的...即使在NodeJS中,所有代码都是同步执行的,除非您显式调用async方法...如果一切都以异步方式完成,则将一事无成。同样,偏爱异步方法并不意味着冗长的计算不会阻塞服务器。这是一个选择。Node的制造商选择提供同步文件系统方法以及异步方法的事实只是表明,这些方法也存在一个地方。

2
我们在哪里可以找到您正在谈论的“ Unix Shell仿真库”?
弗洛里安2014年

@Florian他的意思是ShellJS
xst 2015年

Answers:


153

Node.js(从0.12版开始-已有一段时间)支持execSync

child_process.execSync(command[, options])

您现在可以直接执行以下操作:

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

它会做您期望的。(默认将I / O结果通过管道传递给父进程)。请注意,您spawnSync现在也可以。


6
经过十个小时的绝望。感谢DUDE
Tom Dev

如何与该子流程断开连接?
朱利安·索托(JulianSoto)'18


54

请参阅execSync库。

使用node-ffi相当容易。我不建议用于服务器进程,但对于一般的开发实用程序,它可以完成任务。安装库。

npm install node-ffi

示例脚本:

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

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

[EDIT 2012年6月:如何获取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'));

2
您实际上将如何stdout从中发送任何信息?我所能得到的是流程退出代码
Mark Kahn

@cwolves:我认为异步会更好。(Ivo的答案
pvorb

@pvorb-是的,除非您不能使用async :)
Mark Kahn

1
有充分的理由不对每个钉子使用异步锤。例如,模板引擎在Express 3中是异步的,并且助手功能(本地)需要同步。如果这些辅助函数需要动态地动态编译更少的文件,该怎么办?
mgutz 2012年

8
我不知道为什么这个简单execSync的东西不属于child_process。我认为应该。
MichaelHärtl

31

使用ShellJS模块。

exec函数,不提供回调。

例:

var version = exec('node -v').output;

2
请注意,在撰写本文时,文档提到exec()对于长进程而言,同步是CPU密集型的。
Aram Kocharyan

1
选项,{silent:true}是关键
尼克(Nick)

1
这样做(不是提供较短的语法)不是吗?const execSync = require('child_process').execSync; code = execSync('node -v');
user2503764

23

node.js中有一个出色的流控制模块,称为asyncblock。如果适合您的情况将代码包装在函数中,则可以考虑以下示例:

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...');
});

1
他明确询问了同步版本,而不是控制流库。
Alexey Petrushin

22
@AlexeyPetrushin这里的每个问题都是关于目标的,而不是关于实现目标的特定方法。不过谢谢你的投票。
2013年

1
同样,这对于Windows用户来说是一个非常有用的答案。在Windows上安装exec-syncffi在Windows上安装会产生巨大的开销(VC ++,SDK,Python等),但这要轻一些。
Mendhak 2014年

10

这是不可能的Node.js,都child_process.spawnchild_process.exec从地面建是异步。

有关详细信息,请参见:https : //github.com/ry/node/blob/master/lib/child_process.js

如果您真的想进行此阻止,然后将需要执行的所有操作都放在回调中,或者建立自己的队列以阻止方式进行处理,那么我想您可以将Async.js用于此任务。

或者,如果您有太多时间花在自己的Node.js上,请自行修改。


12
奇怪,因为文件系统模块具有同步调用。为什么不还执行?
Alfred 2010年

4
@Alfred同步FS调用主要在其中,用于在程序启动时加载配置。
伊沃·韦策尔

3
@IvoWetzel-tisk tisk ...我们不是学会了从不说不可能的事吗?;)请在下面查看我的解决方案。
马库斯·波普

1
@IvoWetzel“同步FS调用...”-正确,有时您想发出一个命令,以便在程序启动时进行编译,然后继续完成。-假设存在同步FS调用,没有同步执行程序。确实看起来像是疏忽。我全都是异步的,但是同步确实有其优点和用例。当然,必须以明智的方式使用它。

异步是可以的,但是如果WidgetB取决于WidgetA的最终结果,那么世界上所有的异步都将无法完成工作。有时过程必须是同步的。尝试异步烹饪。;)
Lloyd Sargent 2014年

9

这是我发现的最简单的方法:

exec-Synchttps : //github.com/jeremyfa/node-exec-sync
(不要与execSync混淆。)
同步执行shell命令。将此用于迁移脚本,cli程序,而不用于常规服务器代码。

例:

var execSync = require('exec-sync');   
var user = execSync('echo $USER');
console.log(user);


5

您可以使用纤维来实现。例如,使用我的Common Node库,代码如下所示:

result = require('subprocess').command('node -v');

3

我习惯"synchronous"在回调函数的末尾实现东西。不太好,但是可以。如果需要实现一系列命令行执行,则需要包装exec到某些命名函数中并递归调用它。这种模式似乎对我有用:

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

3

我有一个类似的问题,最终我为此写了一个节点扩展。您可以检出git存储库。它是开源的,免费的,而且还不错!

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

ExecXI是一个用C ++编写的节点扩展,用于一个接一个地执行shell命令,并将命令的输出实时输出到控制台。存在可选的链接和非链接方式;这意味着您可以选择在命令失败(链接)后停止脚本,也可以继续执行,好像什么都没发生!

使用说明位于ReadMe文件中。随时提出请求或提交问题!

编辑: 但是,它还没有返回标准输出……只是实时输出它们。 现在可以了。好吧,我今天才发布它。也许我们可以在此基础上。

无论如何,我认为值得一提。


这正是我一直在寻找的东西。感谢您抽出宝贵的时间来做到这一点。
thealfreds 13-4-16

我还没有弄清楚和实现的唯一事情是不同nodejs版本的构建配置。我想我是在节点0.8(travis-ci.org/aponxi/npm-execxi/builds/5248535)上写的,只要npm install成功(换句话说,就是编译插件),那么进行生产就很好了。除了针对不同的nodejs版本之外,我还要说它已经针对生产进行了修补,或者说非常稳定。如果有任何错误,您可以发送请求请求或在github上发布问题:)
Logan

1

您可以在nodejs中执行同步Shell操作,如下所示:

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\\"));

编辑-此示例适用于Windows环境,必要时根据自己的Linux需求进行调整


是的,并且尽可能快地调用您的CPU ...但是,当您“需要”做一些邪恶的事情时,撒旦是您的好人吗?
Marcus Pope

好吧,这是纯粹的邪恶,但是很棒。我需要它来处理文件系统事件browserify.on('register'),该事件没有回调。拯救了我的一天!
罗伯特·古尔德

您能解释一下为什么这在JavaScript引擎上有效吗?难道不应该在同一滴答声中无限执行while循环,而exec在下一个循环执行吗?
badunk

通过繁忙的等待来最大化CPU的设计是错误的。
路易(Louis)

@ Louis-DominiqueDubeau当然可以,但实际上没有任何选择不依赖于某些第三方来源,这些来源可能与跨平台兼容。这也不是CPU的最大消耗,因为OS不会将nodejs进程的优先级完全赋予。我认为shell操作的同步实现即将出现,或者也许已经在这里。
Marcus Pope

1

实际上,我遇到了这样一种情况,我需要从package.json预安装脚本中依次运行多个命令,使其在Windows和Linux / OSX上都可以运行,所以我不能依赖非核心模块。

这就是我想出的:

#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()

您可以像这样使用它:

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

编辑:正如所指出的,这实际上并不返回输出,也不允许您在Node程序中使用命令的结果。另一个想法是使用LiveScript回调。 http://livescript.net/


谢谢,但这不是答案,因为您的代码异步地
串行

如果您需要按照我的示例同步执行一系列命令,那么它将起作用。我想我还是误解了你的问题,对不起。我在答案中添加了另一个想法。
Jason Livesay
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.