1

I have the following code.

router.post('/',function(req, res, next) {
        var id=req.body.id;
        console.log(id);

    const testscript = exec('sh script.sh');

    testscript.stdout.on('data', function(data){
        console.log(data);
        // sendBackInfo();
    });

    testscript.stderr.on('data', function(data){
        console.log(data);
        // triggerErrorStuff();
    });

        res.redirect('/Visualise');
});

I need to completely execute my shell script and then redirect to '/Visualise'. Some part of code in the next page uses the output file generated by shell script. It is redirecting before executing the shell script completely and i am getting a file not found error. How can i execute synchronously. Please make the required changes in the above code to do so. Thank you.

vatsa
  • 29
  • 1
  • 5
  • 1
    move `res.redirect('/Visualise');` to `testscript.stdout.on` block – wdetac Mar 29 '18 at 07:47
  • 1
    Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – str Mar 29 '18 at 07:47
  • Please read the duplicate and make the required changes by yourself :) The question is not 100% identical, but read the answers and you will see that the exact same principles apply. – str Mar 29 '18 at 07:48
  • you can either use Promise or await. – Harshal Patil Mar 29 '18 at 09:42

2 Answers2

1

You can use child_process.execSync for this, it returns stdout:

child_process.execSync(command[, options])

e.g.

const child_process = require('child_process');
var response = child_process.execSync('sh script.sh');

Response will be a Buffer object.

Documention is here:

https://nodejs.org/api/child_process.html#child_process_child_process_execsync_command_options

Beware that if the script times out or has a non-zero exit code, this method will throw an error, so you might want to wrap with a try / catch.

To add this to your code do this:

const child_process = require('child_process');

router.post('/',function(req, res, next) {
        var id=req.body.id;
        console.log(id);    

        try
        {
            var response = child_process.execSync('sh script.sh');
            console.log('Script response: ', response.toString());
        }
        catch (error) {
            console.error('An error occurred: ', error);
        }

        res.redirect('/Visualise');
});

A better pattern is to do this asynchronously:

const child_process = require('child_process');

router.post('/',function(req, res, next) {
        var id=req.body.id;
        console.log(id);    

        child_process.exec('sh script.sh', (error, stdout) => {
            if (error) {
                console.error(`exec error: ${error}`);
                return;
            }
            console.log(`stdout: ${stdout}`);

            res.redirect('/Visualise');
        });
});

NB: Never pass unsanitized user input to this function, it's a big security risk.

Terry Lennox
  • 17,423
  • 2
  • 18
  • 28
  • 2
    it is **never** a good idea to use sync versions of functions in an asynchronous environment (such as an http request handler), as it blocks the main loop and prevents other events from being handled for the duration of the call. – Darkhogg Mar 29 '18 at 10:16
  • @Darkhogg, I agree with you, I was actually wary about posting that answer, though it's what the OP asked. I will update the answer. – Terry Lennox Mar 29 '18 at 10:19
1

What you really want is not to execute an script synchronously, as that would block the event loop and prevent other requests from running. Instead, you need to (asynchronously) wait for your script to finish.

To do that, write the redirect inside the exit event of the child process, not on the body of your handler:

router.post('/', function(req, res, next) {
    // ...whatever

    const testscript = exec('sh script.sh');

    // ...stdouts

    testscript.on('exit', function (code, signal) {
        res.redirect('/Visualise');
    });
});
Darkhogg
  • 12,286
  • 4
  • 20
  • 24