Question

I have a node js server, which triggers a shell script that I have. The code is as follows (simple Hello World example):

var http = require("http");    

function onRequest(request, response) {
    console.log("Request received.");
    response.writeHead(200, {"Content-Type": "text/plain"});

    var spawn = require('child_process').spawn;
    //executes my shell script - main.sh when a request is posted to the server
    var deploySh = spawn('sh', [ 'main.sh' ]);
    //This lines enables the script to output to the terminal
    deploySh.stdout.pipe(process.stdout);

    //Simple response to user whenever localhost:8888 is accessed
    response.write("Hello World");
    response.end();
}

http.createServer(onRequest).listen(8888);
console.log("Server has started.");

The code works perfectly - A server is started and whenever the page is loaded (request) in the browser, the shell script runs on the server and outputs its results on the terminal.

Now, I want to display that result back to the client browser instead of just "Hello World". How to do that? Please note that the script takes 4 - 5 seconds to execute and generate a result.

Was it helpful?

Solution

You have two options:

Option 1:

Convert your function to use the child_process.exec method instead of child_process.spawn method. This will buffer all data sent to stdout in memory and allow you to send as one chunk across the line to the browser:

var http = require("http"),
    exec = require("child_process").exec;

function onRequest(request, response) {
  console.log("Request received.");
  response.writeHead(200, {"Content-Type": "text/plain"});

  //executes my shell script - main.sh when a request is posted to the server
  exec('sh main.sh', function (err, stdout, stderr) {
    if (err) handleError();

    //Print stdout/stderr to console
    console.log(stdout);
    console.log(stderr);

    //Simple response to user whenever localhost:8888 is accessed
    response.write(stdout);
    response.end();
  });
}

http.createServer(onRequest).listen(8888, function () {
  console.log("Server has started.");
});

Option 2:

If you want to keep the child_process.spawn method, you will need to introduce real time networking into your stack that pushes data events to the browser upon receiving events from the child process. Take a look at socket.io or (recommended) SockJS for setting up real time communication between your server and client.

Side Note:

I wanted to point out a flaw in your code above that will end up hurting you in the long run in your future endeavors with node.

function onRequest(request, response) {
  console.log("Request received.");
  response.writeHead(200, {"Content-Type": "text/plain"});

  var spawn = require('child_process').spawn;

In the last line above, you are requiring the child_process module on each request. Not only does this add unnecessary overhead to each request, you will end up running into module caching issues if you are not careful. It is recommended that all calls to require happen at the top of the module scope and no where else. The only time its "acceptable" to make require calls in the middle of your logic is when building truly synchronous CLI tools — but even then its best to keep all requires organized at the top of your module scope for readability.

OTHER TIPS

 var http = require('http'),
 process = require('child_process');


var server =  http.createServer(function (req,res){

    res.writeHead(200,{'content-type':'text/html'});
        process.exec('ls',function (err,stdout,stderr) {
            if(err){
                console.log("\n"+stderr);
                res.end(stderr);
            } else {
                    console.log(stdout);
                    res.end("<b>ls displayes files in your directory</b></br>"+stdout+"</br>"); 
                }
            }); 

 }).listen(3000);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top