Execute and get the output of a shell command in node.js
Asked Answered
B

10

190

In a node.js, I'd like to find a way to obtain the output of a Unix terminal command. Is there any way to do this?

function getCommandOutput(commandString){
    // now how can I implement this function?
    // getCommandOutput("ls") should print the terminal output of the shell command "ls"
}
Bair answered 17/10, 2012 at 18:41 Comment(5)
Is this a duplicate, or does it describe something completely different? #7183807Bair
This might interest you.Septa
Use npmjs.com/package/cross-spawnTriparted
See Also: Execute a command line binary with Node.jsRosana
For anyone who wants to execute a command on button click, check the next.js way here: #71647484Nicolle
B
209

This is the method I'm using in a project I am currently working on.

var exec = require('child_process').exec;
function execute(command, callback){
    exec(command, function(error, stdout, stderr){ callback(stdout); });
};

Example of retrieving a git user:

module.exports.getGitUser = function(callback){
    execute("git config --global user.name", function(name){
        execute("git config --global user.email", function(email){
            callback({ name: name.replace("\n", ""), email: email.replace("\n", "") });
        });
    });
};
Bat answered 17/10, 2012 at 18:47 Comment(7)
Is it possible to make this function return the command's output? (That's what I was trying to do.)Bair
thats what that code does. take a look at the example at the edit I have just madeBat
@AndersonGreen You wouldn't want the function to return normally with the "return" keyboard, because it is running the shell command asynchronously. As a result, it's better to pass in a callback with code that should run when the shell command is complete.Anaglyph
Ouch, your first sample ignores the possibility of an error when it calls that callback. I wonder what happens to stdout if there is an error. Hopefully deterministic and documented.Fabe
what is the point of the execute wrapper?Quicken
You should mention how to exec commands with user supplied arguments safely.Quicken
@Boris you are free to edit the 9 years old answerBat
S
93

If you're using node later than 7.6 and you don't like the callback style, you can also use node-util's promisify function with async / await to get shell commands which read cleanly. Here's an example of the accepted answer, using this technique:

const { promisify } = require('util');
const exec = promisify(require('child_process').exec)

module.exports.getGitUser = async function getGitUser () {
  // Exec output contains both stderr and stdout outputs
  const nameOutput = await exec('git config --global user.name')
  const emailOutput = await exec('git config --global user.email')

  return { 
    name: nameOutput.stdout.trim(), 
    email: emailOutput.stdout.trim()
  }
};

This also has the added benefit of returning a rejected promise on failed commands, which can be handled with try / catch inside the async code.

Scorch answered 14/5, 2018 at 16:30 Comment(2)
Have you tried this? I'm getting { stdout: string, stderr: string } as a result for the await exec(...)Kirakiran
Yeah, I should have clarified that this gives you the full shell output, including both stdout and stderr. If you want just the output, you could change the last line to: return { name: name.stdout.trim(), email: email.stdout.trim() }.Scorch
M
42

You're looking for child_process

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

child = exec(command,
   function (error, stdout, stderr) {
      console.log('stdout: ' + stdout);
      console.log('stderr: ' + stderr);
      if (error !== null) {
          console.log('exec error: ' + error);
      }
   });

As pointed out by Renato, there are some synchronous exec packages out there now too, see sync-exec that might be more what yo're looking for. Keep in mind though, node.js is designed to be a single threaded high performance network server, so if that's what you're looking to use it for, stay away from sync-exec kinda stuff unless you're only using it during startup or something.

Mroz answered 17/10, 2012 at 18:44 Comment(9)
In this case, how can I obtain the output of the command? Is is "stdout" that contains the command-line output?Bair
Also, is it possible to do something similar without using a callback?Bair
Correct, stdout contains the output of the program. And no, it's not possible to do it without callbacks. Everything in node.js is oriented around being non-blocking, meaning every time you do IO you're going to be using callbacks.Mroz
Note that if you're looking for using javascript to do scripty kinda things where you really want to wait on output and that sort of thing, you might look at the v8 shell, d8Mroz
@Mroz there are some Sync methods natively available, even so IMHO it should be avoidedBat
@Mroz Is the v8 shell compatible with existing node.js libraries?Bair
I haven't tried, but I would guess no. See my edits about sync-exec, it sounds like it may let you do what you want in node.js.Mroz
Trying to capture stdout and and set it to var. Tried things like returning stdout or setting it to externally defined variable and that's not working. I've been searching the net for two days trying to find a simple way of running a command and capturing the output without using something like exec-sync, as I want to be able to run the command async.Anchylose
i like this solution but I think there may be an error on the first line. It worked after I dropped the .exec part on ` require('child_process').exec;`Fulmer
L
32

Requirements

This will require Node.js 7 or later with a support for Promises and Async/Await.

Solution

Create a wrapper function that leverage promises to control the behavior of the child_process.exec command.

Explanation

Using promises and an asynchronous function, you can mimic the behavior of a shell returning the output, without falling into a callback hell and with a pretty neat API. Using the await keyword, you can create a script that reads easily, while still be able to get the work of child_process.exec done.

Code sample

const childProcess = require("child_process");

/**
 * @param {string} command A shell command to execute
 * @return {Promise<string>} A promise that resolve to the output of the shell command, or an error
 * @example const output = await execute("ls -alh");
 */
function execute(command) {
  /**
   * @param {Function} resolve A function that resolves the promise
   * @param {Function} reject A function that fails the promise
   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
   */
  return new Promise(function(resolve, reject) {
    /**
     * @param {Error} error An error triggered during the execution of the childProcess.exec command
     * @param {string|Buffer} standardOutput The result of the shell command execution
     * @param {string|Buffer} standardError The error resulting of the shell command execution
     * @see https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback
     */
    childProcess.exec(command, function(error, standardOutput, standardError) {
      if (error) {
        reject();

        return;
      }

      if (standardError) {
        reject(standardError);

        return;
      }

      resolve(standardOutput);
    });
  });
}

Usage

async function main() {
  try {
    const passwdContent = await execute("cat /etc/passwd");

    console.log(passwdContent);
  } catch (error) {
    console.error(error.toString());
  }

  try {
    const shadowContent = await execute("cat /etc/shadow");

    console.log(shadowContent);
  } catch (error) {
    console.error(error.toString());
  }
}

main();

Sample Output

root:x:0:0::/root:/bin/bash
[output trimmed, bottom line it succeeded]

Error: Command failed: cat /etc/shadow
cat: /etc/shadow: Permission denied

Try it online.

Repl.it.

External resources

Promises.

child_process.exec.

Node.js support table.

Lindsay answered 8/8, 2019 at 20:35 Comment(1)
This is the cleanest option in my opinion and reminiscent of github.com/shelljs/shelljs (but simpler).Rusel
C
23

Thanks to Renato answer, I have created a really basic example:

const exec = require('child_process').exec

exec('git config --global user.name', (err, stdout, stderr) => console.log(stdout))

It will just print your global git username :)

Curable answered 17/7, 2018 at 13:15 Comment(0)
L
6

You can use the util library that comes with nodejs to get a promise from the exec command and can use that output as you need. Use destructuring to store the stdout and stderr in variables.

const util = require('util');
const exec = util.promisify(require('child_process').exec);

async function lsExample() {
  const {
    stdout,
    stderr
  } = await exec('ls');
  console.log('stdout:', stdout);
  console.error('stderr:', stderr);
}
lsExample();
Longhair answered 25/2, 2021 at 8:4 Comment(1)
Basically the same as https://mcmap.net/q/54987/-execute-and-get-the-output-of-a-shell-command-in-node-jsHyperbole
H
4

you can use ShellJS package.
ShellJS is a portable (Windows/Linux/OS X) implementation of Unix shell commands on top of the Node.js API.
see: https://www.npmjs.com/package/shelljs#execcommand--options--callback

import * as shell from "shelljs";

//usage:
//exec(command [, options] [, callback])

//example:
const version = shell.exec("node --version", {async: false}).stdout;
console.log("nodejs version", version);
Horatio answered 13/7, 2021 at 5:15 Comment(2)
I get "TypeError: shell.exec is not a function" when running this solution. I'm using Node v16.10.0 on Ubuntu 20.04 LTS. Any ideas?Driving
Look at the documentation, e.g. github.com/shelljs/shelljs#examples. There the first line looks like this: import shell from 'shelljs';.Carmine
S
1

Here's an async await TypeScript implementation of the accepted answer:

const execute = async (command: string): Promise<any> => {
  return new Promise((resolve, reject) => {
    const exec = require("child_process").exec;
    exec(
      command,
      function (
        error: Error,
        stdout: string | Buffer,
        stderr: string | Buffer
      ) {
        if (error) {
          reject(error);
          return;
        }
        if (stderr) {
          reject(stderr);
          return;
        } else {
          resolve(stdout);
        }
      }
    );
  });
};
Spelt answered 6/9, 2022 at 0:30 Comment(1)
Why not use util.promisify?Hyperbole
W
0

I believe the easiest and fastest way to accomplish it is using the response given by Damjan Pavlica.

Expanding over his answer, if you would like to implement separate handler functions for different listeners, Nodejs in its version LTS v20 provides the spawn method of ChildProcess.

const { spawn } = require('node:child_process');
const ls = spawn('ls', ['-lh', '/usr']);

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

https://nodejs.org/dist/latest-v20.x/docs/api/child_process.html#asynchronous-process-creation

In my particular case I was interested in achieving this in a Windows OS environment and using Powershell.

Most of the answers here worked with only one caveat, your command must start with powershell.exe

const { exec } = require('node:child_process');

exec('powershell.exe ls -Name', (err, stdout, stderr) => {
  console.log(stdout);
});
Winepress answered 22/11, 2023 at 3:44 Comment(0)
S
-1

If anyone is looking for a CLI way to run a node script without a file, use the -e argument and put your node code in quotes like this:

node -e "console.log(process.env.PATH.split(':'))"

This is an easy way to inspect your path. :)

Soundboard answered 9/8, 2023 at 15:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.