How to execute a shell command using Node.js

By

Learn how to run a shell command from a Node.js script by importing the child_process module and calling child.exec() with the command you want to run.

~~~

Here’s how I ran a shell command from a Node.js script.

First I imported child from child_process:

import * as child from 'node:child_process'

//or 

const child = require('node:child_process')

child_process is a built-in module, so there’s nothing to install. It lets your Node script start other programs, the same way you’d type a command in the terminal.

Then you can call child.exec() like this:

child.exec(`mkdir test`)

That runs mkdir test in a shell and creates a test folder. The command runs asynchronously, so exec() returns right away and your script keeps going without waiting for the folder to be created.

Reading the output

Most of the time you don’t just want to run a command, you want its output. exec() takes a callback that fires when the command finishes. You get an error, the standard output, and the standard error:

child.exec('ls -la', (error, stdout, stderr) => {
  if (error) {
    console.error(error)
    return
  }
  console.log(stdout)
})

stdout holds everything the command printed. Check error first, because it’s set when the command exits with a non-zero code, like when the folder already exists or the command doesn’t exist.

Waiting for it with a Promise

Because exec() uses a callback, it doesn’t fit nicely with async/await. Node ships a Promise-based version through util.promisify:

import { promisify } from 'node:util'

const exec = promisify(child.exec)

const { stdout } = await exec('node --version')
console.log(stdout) //v22.0.0

Now you can await the command and read stdout and stderr off the resolved object.

One thing to be careful about

exec() runs your string through the shell. That’s convenient, because pipes and redirects work, but it’s dangerous if any part of the command comes from user input. Someone could pass test; rm -rf ~ and the shell would happily run both halves.

When the command includes outside input, reach for child.execFile() or child.spawn() instead. Those take the program and its arguments as separate values and skip the shell, so there’s nothing for an injected string to break out of.

Tagged: Node.js · All topics
~~~

Related posts about node: