【问题标题】:Attempting synchronous behaviour with shell script execution in node.js在 node.js 中尝试与 shell 脚本执行同步行为
【发布时间】:2014-07-23 02:28:07
【问题描述】:

我是 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!');
});

【问题讨论】:

    标签: javascript node.js shell


    【解决方案1】:

    您没有异步调用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 参数引入的安全问题。

    【讨论】:

    • 完美,与此相比,我的代码真的很差!现在我有一个不同的问题,它只有在我注释掉 res.writeHead 部分时才会运行。它似乎在 writeHead 之前运行发送。这是我得到的错误:错误:发送后无法设置标头。另外 - 你提到的安全问题是什么 - 我有点迷茫..
    • 嗯,不应该。代码中是否还有其他send()s?我说的漏洞是一个基本的command injection。浏览/free/;/rm+-r+\.(或更好:不要!)阅读this question about escaping
    • 除了“/”路由代码(如上)之外,代码中没有其他 send()。我明白为什么现在这是一个巨大的安全问题!说得通。不过,我并没有真正遵循转义解决方案。我是否需要使用某种正则表达式来排除任何可能无意执行的内容?
    • 不,您需要将字符串参数用引号括起来,并使用正则表达式来转义任何字符串分隔符。或者您只需使用spawn() 而不是exec(),它确实接受一组参数并适当地转义它们。
    • 好的,谢谢。因此,从我读过的内容来看,spawn 会发回一个数据流,而不是在使用 exec 时将其限制为缓冲区大小。我找不到关于自动转义 shell 参数的任何材料。此外,我将不得不捕获导致我回到最初的异步问题的流...... arrghh。有没有机会你可以改用 spawn 为我重写上面的内容?
    【解决方案2】:

    基本上你遇到的问题是你调用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)
    

    【讨论】:

      【解决方案3】:

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

      要修复它,您可以像这样更改 puts 函数 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 变量。

      【讨论】:

      • 感谢您的帮助 advncd,这也有效,但我想我不必要地使用了异步
      猜你喜欢
      • 2010-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-06
      • 2020-06-16
      • 2022-08-18
      • 2019-02-07
      • 1970-01-01
      相关资源
      最近更新 更多