Writing NodeJS cli tools

Written by Kevin Gimbel on , 🍿 4 min. read

Over 2015 and 2016 I got more and more interested in automating my daily workflows with scripts. For this purpose I learned Bash Scripting (or Shell Scripting) and started writing my first scripts to automate repeating tasks, creating apache vhosts files for example. Recently I started exploring how to implement command line tools in NodeJS and this post should give an overview.


If you already know what a Shebang is skip to the next section

Shell Scripts always start with a Shebang on the first line. This line is used to indicate which program is used to execute the following script. For Bash scripts the Shebang is #!/bin/bash. What the OS or program loader does when executing the script is the following. Consider this sample script named hello.sh

echo "Hello World!"

When it is executed from the command line (cli) with ./hello.sh the operating system executes the script (echo "Hello World!") with /bin/bash. Equally, you could run /bin/bash ./hello.sh. When programming a bash script you can also set it to exit on every error, either with set -e or with a shebang similar to #!/bin/bash -e. The screenshot below demonstrates the difference when running a invalid shell script bad.sh.


As you can see without the #!/bin/bash -e shebang the script will execute even after an error occurred - this might not be intended behavior. Anyway, the topic of this post is node and the Shebang in node is essential. NodeJS Scripts are JavaScript and cannot be run - like shell scripts - without an Shebang to tell the system to invoke them with node. The NodeJS Shebang looks a bit different and specifies the "environment" or env with /usr/bin/env. The Shebang then reads #!/usr/bin/env node. Without the Shebang NodeJS scripts will fail immediately after being invoked as seen in the following screenshot.


While it is not necessarily needed for scripts written in in Shell/Bash the Shebang cannot be omitted from NodeJS scripts intended to be run as command line program.

Well, that has been a fun excursion into the world of shell scripting. Back to node!

Writing a tiny node cli script

Since we now know how to use a Shebang and how to invoke out Node scripts from the command line we may write a script. Let's keep it simple. We will greet the person executing the script.

#!/usr/bin/env node

const sayHello = () => {
console.log(`Hello, ${process.env.USER}`)


Let's save the script and run it with $ node hello.js from the command line.


Now that's interesting: The script greets the currently logged-in user! That's the case because we make use of the process variable which holds information about the current environment - the env object for example holds your environment variables. Add a new function to hello.js to see the output.

const printEnv = () => {


You will see the USER variable is part of the environment object and holds the name of the logged-in user. This way node scripts can access terminal variables and use them. Run export _test=123 and then node hello.js - _test is now part of the environment. By using the Environment we can make a script execute different tasks depending on the environment, as seen in the next snippet.

const log = (message, level = 0) => {
if(process.env.NODE_ENV == 'production') {
writeLogFile(message, level)
} else {

const writeLogFile = (message, level) => {
console.error('Not implemented')

log("This is a test!")

Here we have a log function which either runs the writeLogFile function when in production mode or logs to the console if we are in any other mode (development for example.)


As you can see in the screenshot depending on the NODE_ENV variable a different function is executed. The environment can be set inline ($ export NODE_ENV=production) for the duration of the shell session or it can be set permanent, e.g. by adding it to the shell runtime file (~/.zshrc or ~/.bashrc). You may also noticed the process.exit(1) inside the writeLogFile function. The exit function enables us to use exit codes to indicate if our script ended successful or not to other processes. If a script exits with 0 it was successful, everything else is considered an error. To see how a script exits you can run echo $? inside a terminal.

You can read more about exit codes in the resources of TLDP.

So far we learned that Node CLI scripts:

  • need a shebang, #!/usr/bin/env node
  • are written in regular JavaScript
  • can access the environment
  • can exit using process.exit(exit_code)
  • can utilize lots of Node packages

And that's it for today. All the example scripts are available on GitHub.

Hi, I'm Kevin!

I'm a DevOps Engineer with a passion for on automation and monitoring. Before shifting into DevOps and cloud computing I worked as Front-End Developer, which is still a hobby and field of interest for me.

Picture of Kevin Gimbel, in a tiny mirror

I'm very passionated about a variety of games - digital, boardgames, and pen & paper; and also interested in Sci-Fi, Cyberpunk, and dystopian books. You can find out more on the about page.