How to execute shell commands in js
Tags: javascript, beginners, tutorial, node
Published: 2020-08-26 10:06:00

For one of my side projects, I needed to be able to execute certain shell commands with JavaScript. That project is an Electron–React project, in which – thanks to the Electron framework – I have _full access to the Node.js API_ and therefor the Node.js runtime. For the readers that are interested in Electron or in combining Electron and React you can find my blogs on those topics respectively [here](https://dev.to/alexdhaenens/electron-the-future-18nc) and [here](https://dev.to/alexdhaenens/electron-and-react-a-successful-marriage-ncf). 
Although it is possible to execute shell commands in JavaScript, there are two important remarks: first that __executing shell commands uses the Node.js API__, so be aware that it only works in an environment that has access to that API (meaning a normal browser runtime won’t work). Secondly, __some shell commands require administration permissions__, so in those cases you’ll need to make sure that the process running your JavaScript code has such permissions.

#The API
The Node.js API has a module called [child_process] (https://nodejs.org/api/child_process.html#child_process_child_process) that offers functions to spawn child processes both in ansynchronous as asynchronous manner. One of those functions that are available is the _exec function_. It has the following signature:

```javascript
exec(command[, options][, callback])
```

With the parameters: _command_ as a string, _options_ as an object with various options (see the [documentation]( https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback) for more information) and a _callback function_. The function itself returns a reference to the spawned process (but it is not needed for executing shell commands).

With the _exec_ function we can create a custom function with two different callbacks:

```javascript
const { exec } = require('child_process');

export const executeCommand = (cmd, successCallback, errorCallback) => {
  exec(cmd, (error, stdout, stderr) => {
    if (error) {
     // console.log(`error: ${error.message}`);
      if (errorCallback) {
        errorCallback(error.message);
      }
      return;
    }
    if (stderr) {
      //console.log(`stderr: ${stderr}`);
      if (errorCallback) {
        errorCallback(stderr);
      }
      return;
    }
    //console.log(`stdout: ${stdout}`);
    if (successCallback) {
      successCallback(stdout);
    }
  });
};
```

Although not required, using such a function is much handier and cleaner as you can use a different callback functions for success and error. In addition, there is a single point where you can turn on or off logging for all your commands.

#Creating command functions
Since we got our base function for executing commands we can create now different functions for the different commands the code needs to execute. Depending which operating system you are targeting, it can be possible that other (shell)commands are needed (e.g. the _dir_ command on windows and the _ls_ command on linux). For the sake of an example you can get the current git branch with the following git command: 

```bash
git -C “folder” rev-parse --abbrev-ref HEAD
```

We can create a custom function for this, accepting a folder and two callbacks to execute:

```javascript
export const getGitBranchCommand = (folder, success, error) => {
  executeCommand(
    `git -C ${folder} rev-parse --abbrev-ref HEAD`,
    branch => success(branch),
    errormsg => error(errormsg)
  );
}; 
```

This functino will either call the _success_ callback with the output of the shell command (which is the branch name) or call the _error_ callback with the message the command returned when failing.

Some shell commands print a lot of text to the stout stream, so for those commands you’ll need to apply a regex to parse the data you want from that output.

#Combining with state frameworks
A lot of applications use a state framework to keep the current state of your app. Chances are that you, the reader, use such a framework in your project and you’ll want to store the result of the commands you executed in that state. In my example I’m using [Redux](https://redux.js.org/) but you can follow a similar approach for other frameworks. 
By using the _getGitBranchCommand_ shown above you can create a new function specific for the Redux framework:

```javascript
export const getGitBranch = (folder, dispatch) => {
  getGitBranchCommand(folder, branch =>
    dispatch(setFocusProjectGitBranch(branch), () => {})
  );
};
```

Now you have a function that accepts a folder and the dispatch function (needed for dispatching actions in redux). This function can now be used anywhere in your application. In the code snippet above I’ve used the _setFocusProjectGitBranch_ function, which is an action creator (if you don't know what that is, no worries it is Redux specific). Also, on a side node, the error callback is an empty function since I don’t need the error message (yet).

#Architecture summary
I would like to summarize the blogpost by discussing the architecture used:

```javascript
getGitBranch(folder,dispatch) 
=> getGitBranchCommand(folder, success, error) 
=> executeCommand(cmd, successCallback, errorCallback) 
=> exec(command[, options][, callback])
```

The _executeCommand_ is a general function for executing any command using the _exec_ function from the __child_processes node module__. That function is used by the _getGitBranchCommand_, a function specific designed for getting the git branch. Lastly the highest function, is the one exposed to my whole application and is a state management framework dependent function. It executes the earlier mentioned _getGitBranchCommand_ function and stores the result in the state, by using the state management’s api.

Using this architecture has the benefit that, when you would reuse the code in another project but with another state management framework, you only need to replace the _getGitBranch function_. Additionally, if you would for example support other operating systems, which could require different commands to do the same thing, you’ll only need to replace the _getGitBranchCommand_ function.