尝试在node.js中执行shell脚本的同步行为

时间:2014-06-02 21:24:52

标签: javascript node.js shell

我对node.js很新,我将它用作服务器从浏览器接收http.get请求,进行一些处理并将结果返回给浏览器。

处理实际上是使用phantom.js / casper.js抓取网站,并且由于在node.js中使用casper.js的复杂性,我使用shell脚本将其作为子进程执行。

当我运行下面的代码并从浏览器发出请求时,响应似乎在shell脚本运行之前出现。我尝试使用异步库来运行系列中的步骤,以便run.sh运行并在它添加到响应之前返回一个值,但它似乎不像那样工作。谁能发现我的愚蠢错误?

提前致谢!

var express = require('express');
var app = express();
var sys = require('sys');
var exec = require('child_process').exec;
var async = require('async');
var auth = express.basicAuth('test', 'pass');
var server = app.listen(3000, function() {
    console.log('Listening on port %d', server.address().port);
});
var result;


//app.use(auth);

app.get('/free/:u/:p', auth, function(req, res) { 
    async.series([
        function(callback){
            var puts = function (error, stdout, stderr) {
                result = stdout;
             }
             exec("./run.sh " + req.params.u + " " + req.params.p, puts);
             callback(null);
        },
        function(callback){
             res.writeHead(200,{'Content-Type':'application/json'});
             res.send(result)
             callback(null);
        }
    ]);
});

app.get('/', function(req, res) {
  res.send('Hello!');
});

3 个答案:

答案 0 :(得分:0)

您没有异步调用callback(null)。您需要将其放入puts

此外,您不应该使用全局results变量(它甚至可以跨请求进行全局变换,可能会将数据泄露给不同的客户端 - 唉!)。并且没有理由在这个简单的链条中使用async.js。

app.get('/free/:u/:p', auth, function(req, res) { 
    exec("./run.sh " + req.params.u + " " + req.params.p, function (error, stdout, stderr) {
       res.writeHead(200,{'Content-Type':'application/json'});
       res.send(stdout);
    });
});

请注意不转义shell参数引入的安全问题。

答案 1 :(得分:0)

基本上你遇到的问题是你正在调用exec,好像它是一个同步函数,实际上它是异步的。

所以你应该这样做:

exec("./run.sh " + req.params.u + " " + req.params.p, puts, function(){
   callback(null);
});

或者只是这样做,因为无论如何你都不会关心异步系列中函数传递的函数参数。

exec("./run.sh " + req.params.u + " " + req.params.p, puts, callback)

答案 2 :(得分:0)

原因是你运行shell命令然后立即调用运行下一个函数(响应)的回调。

要解决此问题,您可以像这样更改置位函数puts并移除callback(null)

var express = require('express');
var app = express();
var sys = require('sys');
var exec = require('child_process').exec;
var async = require('async');
var auth = express.basicAuth('test', 'pass');
var server = app.listen(3000, function() {
    console.log('Listening on port %d', server.address().port);
});
var result;


//app.use(auth);

app.get('/free/:u/:p', auth, function(req, res) { 
    async.series([
        function(callback){
            var puts = function (error, stdout, stderr) {
                result = stdout;
                callback(null); //added here
             }
             exec("./run.sh " + req.params.u + " " + req.params.p, puts);
             //callback(null);  //commented
        },
        function(callback){
             res.writeHead(200,{'Content-Type':'application/json'});
             res.send(result)
             callback(null);  
        }
    ]);
});

app.get('/', function(req, res) {
  res.send('Hello!');
});

虽然这种方法有效,但最好使用async.waterfall代替async.series。它允许您将每个函数的结果传递给下一个函数。因此,您不需要拥有全局result变量。