217

I want to write a JavaScript function which will execute the system shell commands (ls for example) and return the value.

How do I achieve this?

Pavel Fedotov
  • 748
  • 1
  • 7
  • 29
Sunil Kumar Sahoo
  • 53,011
  • 55
  • 178
  • 243

16 Answers16

192

I'll answer assuming that when the asker said "Shell Script" he meant a Node.js backend JavaScript. Possibly using commander.js to use frame your code :)

You could use the child_process module from node's API. I pasted the example code below.

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

exec('cat *.js bad_file | wc -l',
    function (error, stdout, stderr) {
        console.log('stdout: ' + stdout);
        console.log('stderr: ' + stderr);
        if (error !== null) {
             console.log('exec error: ' + error);
        }
    });
starball
  • 20,030
  • 7
  • 43
  • 238
Josh
  • 2,057
  • 1
  • 12
  • 5
  • 23
    Except for one thing. You'll get the error "child is not a function". The call to exec() executes the command - no need to call child(). Unfortunately, the callback isn't called whenever the child process has something to output - it is called only when the child process exits. Sometimes that's OK and sometimes it's not. – John Deighan Apr 26 '16 at 12:52
  • 7
    To avoid callbacks, you can use [execSync](https://stackoverflow.com/a/52575123/1269037). – Dan Dascalescu Sep 30 '18 at 06:10
  • 3
    Who talked about a browser ? It only says JavaScript, nothing more. – Virus721 Mar 10 '20 at 09:14
  • @Josh How to pass credentials here (user name and password)? – Shabar Dec 11 '22 at 09:51
136

I don't know why the previous answers gave all sorts of complicated solutions. If you just want to execute a quick command like ls, you don't need async/await or callbacks or anything. Here's all you need - execSync:

const execSync = require('child_process').execSync;
// import { execSync } from 'child_process';  // replace ^ if using ES modules

const output = execSync('ls', { encoding: 'utf-8' });  // the default is 'buffer'
console.log('Output was:\n', output);

For error handling, add a try/catch block around the statement.

If you're running a command that takes a long time to complete, then yes, look at the asynchronous exec function.

Alex Hurst
  • 184
  • 8
Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
  • 4
    Does execSync works with Mac, Linux and Windows commands? – Naazneen Jatu Sep 11 '20 at 15:15
  • Confirmed for windows. Also, I see that there's a `shell` option for specifying "Shell to execute the command with." Default: '/bin/sh' on Unix, process.env.ComSpec on Windows. – Kerry Randolph Jun 18 '21 at 20:44
  • Please do not use synchronous external operations, because your server will not be able to serve other requests until it is finished. – sarkiroka Jun 16 '22 at 07:58
71

...few year later...

ES6 has been accepted as a standard and ES7 is around the corner so it deserves updated answer. We'll use ES6+async/await with nodejs+babel as an example, prerequisites are:

Your example foo.js file may look like:

import { exec } from 'child_process';

/**
 * Execute simple shell command (async wrapper).
 * @param {String} cmd
 * @return {Object} { stdout: String, stderr: String }
 */
async function sh(cmd) {
  return new Promise(function (resolve, reject) {
    exec(cmd, (err, stdout, stderr) => {
      if (err) {
        reject(err);
      } else {
        resolve({ stdout, stderr });
      }
    });
  });
}

async function main() {
  let { stdout } = await sh('ls');
  for (let line of stdout.split('\n')) {
    console.log(`ls: ${line}`);
  }
}

main();

Make sure you have babel:

npm i babel-cli -g

Install latest preset:

npm i babel-preset-latest

Run it via:

babel-node --presets latest foo.js
Mirek Rusin
  • 18,820
  • 3
  • 43
  • 36
33

This depends entirely on the JavaScript environment. Please elaborate.

For example, in Windows Scripting, you do things like:

var shell = WScript.CreateObject("WScript.Shell");
shell.Run("command here");
Matt
  • 43,482
  • 6
  • 101
  • 102
  • 21
    Is it possible to do the same thing in a Unix-like operating system such as Linux? – Anderson Green Sep 05 '12 at 16:21
  • 10
    That's what I was looking for. It's annoying all those people talking about their Node.js. Who asked for Node.js here ? No one did. – Virus721 Mar 10 '20 at 09:12
  • Would you know how to get some result (say, exit code), of the newly run command? – sighclone Apr 10 '23 at 13:09
  • @sighclone I answered this nearly 14 years ago, can't say I've kept up with WScript. But the docs say the Run() method returns the exit code. https://www.vbsedit.com/html/6f28899c-d653-4555-8a59-49640b0e32ea.asp – Matt Apr 10 '23 at 13:49
21

In a nutshell:

// Instantiate the Shell object and invoke its execute method.
var oShell = new ActiveXObject("Shell.Application");

var commandtoRun = "C:\\Winnt\\Notepad.exe";
if (inputparms != "") {
  var commandParms = document.Form1.filename.value;
}

// Invoke the execute method.  
oShell.ShellExecute(commandtoRun, commandParms, "", "open", "1");
musicformellons
  • 12,283
  • 4
  • 51
  • 86
Dana
  • 2,619
  • 5
  • 31
  • 45
  • 11
    [ActiveXObject is available only on IE browser.](http://stackoverflow.com/questions/11101641/activexobject-is-not-defined-and-cant-find-variable-activexobject#answer-11101655) – Christopher Markieta Sep 13 '15 at 18:16
  • 7
    There seems to be a lot of hand-wringing over which web browser this is running in, but folks should realize that JavaScript is also a perfectly valid Windows shell scripting language. – Craig Tullis Feb 03 '16 at 00:48
  • 3
    not familiar with that particular shell "NutShell" ;) – redevill Sep 30 '21 at 21:36
8

Note: These answers are from a browser based client to a Unix based web server.

Run command on client

You essentially can't. Security says only run within a browser and its access to commands and filesystem is limited.

Run ls on server

You can use an AJAX call to retrieve a dynamic page passing in your parameters via a GET.

Be aware that this also opens up a security risk as you would have to do something to ensure that mrs rouge hacker does not get your application to say run: /dev/null && rm -rf / ......

So in a nutshel, running from JS is just a bad, bad idea.... YMMV

Wayne
  • 3,415
  • 1
  • 22
  • 21
  • You essentially can't *in a cross-browser manner.* I believe only IE has hooks into the shell (via WSript/ActiveX). – Justin Johnson Dec 10 '09 at 11:32
  • 1
    All the high vote answers assume it's run in a non-browser context, e.g. Node. The right thing is to clarify the question, not this answer. – Inigo Dec 13 '21 at 18:04
8

With NodeJS is simple like that! And if you want to run this script at each boot of your server, you can have a look on the forever-service application!

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

exec('php main.php', function (error, stdOut, stdErr) {
    // do what you want!
});
keupsonite
  • 399
  • 3
  • 15
  • 1
    To avoid callbacks, for quick commands you can use [execSync](https://stackoverflow.com/a/52575123/1269037). – Dan Dascalescu Sep 30 '18 at 06:09
  • what if want to run `sudo mysql` .?? is it possible ?? if yes, how the password, it is going to ask after this command. – Aman Deep Sep 08 '21 at 16:21
7
function exec(cmd, handler = function(error, stdout, stderr){console.log(stdout);if(error !== null){console.log(stderr)}})
{
    const childfork = require('child_process');
    return childfork.exec(cmd, handler);
}

This function can be easily used like:

exec('echo test');
//output:
//test

exec('echo test', function(err, stdout){console.log(stdout+stdout+stdout)});
//output:
//testtesttest
AliFurkan
  • 487
  • 5
  • 11
5

Here is simple command that executes ifconfig shell command of Linux

var process = require('child_process');
process.exec('ifconfig',function (err,stdout,stderr) {
    if (err) {
        console.log("\n"+stderr);
    } else {
        console.log(stdout);
    }
});
Piotr Wittchen
  • 3,853
  • 4
  • 26
  • 39
Anup Panwar
  • 293
  • 4
  • 11
3

If you are using npm you can use the shelljs package

To install: npm install [-g] shelljs

var shell = require('shelljs');
shell.ls('*.js').forEach(function (file) {
// do something
});

See more: https://www.npmjs.com/package/shelljs

Emma
  • 149
  • 2
  • 8
3

As far as I can tell, there is no built-in function, method or otherwise, in the official ECMAScript specification to run an external process. That said, extensions are allowed, see this note from the spec, for example:

NOTE Examples of built-in functions include parseInt and Math.exp. A host or implementation may provide additional built-in functions that are not described in this specification.

One such "host" is Node.js which has the child_process module. Let's try this code to execute the Linux shell command ps -aux, saved in runps.js, based on the child_process documentation:

const { spawn } = require('child_process');
const ps = spawn('ps', ['-aux']);

ps.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ps.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

ps.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});

Which produces the following example output, running it in docker:

$ docker run --rm -v "$PWD":/usr/src/app -w /usr/src/app node:17-bullseye node ./runps.js
stdout: USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.8 319312 33888 ?        Ssl  11:08   0:00 node ./runps.js
root          13  0.0  0.0   6700  2844 ?        R    11:08   0:00 ps -aux

child process exited with code 0

The thing I like about this module, is that it's included with the Node.js distribution, no npm install ... needed.

If you search the Node.js code in github for spawn you will find references to the implementation in C or C++ in the engine. Modern browsers like Firefox and Chrome would be reluctant to extend JavaScript with such features, for obvious security reasons, even if the underlying engine such as V8 supports it.

On that note, it's better not to run our container as root, let's try the above example again, adding a random user this time.

$ docker run --rm -u 7000 -v "$PWD":/usr/src/app -w /usr/src/app node:17-bullseye node ./runps.js
stdout: USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
7000           1  5.0  0.8 319312 33812 ?        Ssl  11:19   0:00 node ./runps.js
7000          13  0.0  0.0   6700  2832 ?        R    11:19   0:00 ps -aux

child process exited with code 0

Of course that's better but not enough. If this approach is used at all, more precautions must be taken, such as ensuring that no arbitrary user commands can be executed.

Windows 10

My version of Windows 10 still has Windows Script Host which can run JScript on the console with the wscript.exe or cscript.exe programs, i.e. no browser needed. To try it out you can open a PowerShell Windows Terminal. Save the following code into a file which you can call shell.js:

WScript.StdOut.WriteLine("Hallo, ECMAScript on Windows!");
WScript.CreateObject("WScript.Shell").run("C://Windows//system32//mspaint.exe");

And on the command line, run:

cscript .\shell.js

Which shows the following and opens Paint:

Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.

Hallo, ECMAScript on Windows!

Other variations exist. Find the documentation applicable to your preferred JavaScript runtime environment.

Nagev
  • 10,835
  • 4
  • 58
  • 69
2

Another post on this topic with a nice jQuery/Ajax/PHP solution:

shell scripting and jQuery

Community
  • 1
  • 1
August
  • 698
  • 4
  • 12
  • 21
2

In IE, you can do this :

var shell = new ActiveXObject("WScript.Shell");
shell.run("cmd /c dir & pause");
nonozor
  • 865
  • 2
  • 14
  • 24
1

With nashorn you can write a script like this:

$EXEC('find -type f');
var files = $OUT.split('\n');
files.forEach(...
...

and run it:

jjs -scripting each_file.js
Maciej Łoziński
  • 812
  • 1
  • 10
  • 16
0
const fs = require('fs');

function ls(startPath) {
    fs.readdir(startPath, (err, entries) => {
        console.log(entries);
    })
}

ls('/home/<profile_name>/<folder_name>')

The startPath used here is in reference with debian distro

0

Js file

var oShell = new ActiveXObject("Shell.Application");
oShell.ShellExecute("E:/F/Name.bat","","","Open","");

Bat file

powershell -Command "& {ls | Out-File -FilePath `E:F/Name.txt}"`

Js file run with node namefile.js

const fs = require('fs')

fs.readFile('E:F/Name.txt', (err, data) => {
if (err) throw err;

console.log(data.toString());
})

You can also do everything in one solution with an asynchronous function. Directly there could be security problems.