Skip to main content

Command Palette

Search for a command to run...

Build Your Own CLI Word Counter in Node.js

Published
4 min read
Build Your Own CLI Word Counter in Node.js
H

I believe the best way to master a concept is to explain it to someone else. I’m a Full Stack Developer navigating the ecosystems of JavaScript and Python, building everything from responsive frontends to robust backends. I use this space to document my learning journey, break down complex topics, and share practical solutions to the bugs I encounter. Let's learn in public together!

Have you ever wanted to build your own Command Line Interface (CLI) tool?

In this Blog, I’ll create a simple CLI app in Node.js that counts the number of words in a file. Along the way, we’ll explore how Node.js handles command-line arguments with process.argv and how to make our CLI professional using the commander library.

Step 1: A Simple Word Counter

Let’s begin with a very basic Node.js program that reads a file and counts words:

const fs = require("fs");

function countWords(fileName) {
  fs.readFile(fileName, "utf-8", function (err, data) {
    let count = 0;
    for (let i = 0; i < data.length; i++) {
      if (data[i] == " ") {
        count++;
      }
    }
    console.log(count + 1);
  });
}

countWords("a.txt");

This works. If a.txt has text, the script will count its words.
But here’s the problem: this isn’t really a CLI. We can’t do things like node index.js -help, and it only works with a hardcoded file.

Step 2: Making It Dynamic with process.argv

In Node.js, anything passed after the script name in the command line can be accessed using process.argv.

console.log(process.argv);

//In console, if you write "node index.js filePath", then the console output will be 
[
  'C:\\Program Files\\nodejs\\node.exe',
  'C:\\Users\\HarshGupta\\Desktop\\index.js',
  'C:\\Users\\HarshGupta\\Desktop\\a.txt'
]

Here:

  • process.argv[0] → path to Node.js

  • process.argv[1] → path to our script

  • process.argv[2] → the file path we passed

So, we can make our script dynamic like this:

const fs = require("fs");

function countWords(fileName) {
  fs.readFile(fileName, "utf-8", function (err, data) {
    let count = 0;
    for (let i = 0; i < data.length; i++) {
      if (data[i] == " ") {
        count++;
      }
    }
    console.log(count + 1);
  });
}

countWords(process.argv[2]);

Now we can pass any file path dynamically, but still it doesnt feel like a CLI.

Step 3: Turning It Into a Proper CLI with Commander

To add features like --help, --version, and subcommands, we’ll use the commander library.

First install it:

npm install commander

Now, update our code:

const fs = require("fs");
const { Command } = require("commander");

const program = new Command();

program
  .name("Counter")
  .description("Counts the number of words in a file")
  .version("0.0.0");

program
  .command("count")
  .description("Counts the number of words in a file")
  .argument("<file>", "file to count")
  .action((fileName) => {
    fs.readFile(fileName, "utf-8", (err, data) => {
      if (err) {
        console.log(err);
      } else {
        let count = 0;
        for (let i = 0; i < data.length; i++) {
          if (data[i] == " ") {
            count++;
          }
        }
        console.log(`There are ${count + 1} words in ${fileName}`);
      }
    });
  });

program.parse();

Step 4: Running the CLI

Now let’s try it out.

Run:

node index.js count C:\Users\HarshGupta\Desktop\a.txt

Outputs:

There are 5 words in C:\Users\HarshGupta\Desktop\a.txt

We also get built-in CLI support for free:

node index.js --help
Usage: Counter [options] [command]

Counts the number of words in a file

Options:
  -V, --version        output the version number
  -h, --help           display help for command

Commands:
  count <file>         Counts the number of words in a file
  help [command]       display help for command

More to it

You can add as many command you want, like this:

const fs = require("fs");
const {Command} = require("commander");
const program = new Command;

program
    .name("Counter")
    .description("Counts the number of words and lines in  a file")
    .version("0.0.0");

program.command("count")
    .description("Counts the number of words in a file")
    .argument('<file>', 'file to count')
    .action((fileName) => {
        fs.readFile(fileName, "utf-8", (err, data) => {
            if(err){
                console.log(err);
            }
            else{
                let count = 0;
                for(let i = 0; i < data.length; i++){
                    if(data[i] == " "){
                        count++;
                    }
                }
                console.log(`There are ${count + 1} words in ${fileName}`);
            }
        })
    })

program.command("count_lines")
    .description("Counts the number of lines in a file")
    .argument('<file>', 'file to count')
    .action((fileName) => {
        fs.readFile(fileName, "utf-8", (err, data) => {
            if(err){
                console.log(err);
            }
            else{
                let count = 0;
                for(let i = 0; i < data.length; i++){
                    if(data[i] == "\n"){
                        count++;
                    }
                }
                console.log(`There are ${count} lines in ${fileName}`);
            }
        })
    })

    program.parse();

Conclusion

We just built a fully functional CLI word counter from scratch using Node.js. Starting with a simple script, we made it dynamic using process.argv and then transformed it into a professional-grade CLI using commander.

This project might seem small, but the skills you’ve learned here are foundational for building real-world developer tools. Most CLI tools you use daily — like npm, git, or eslint — are built on the same principles.

More from this blog

Javascript With Harsh

63 posts

I am a cse undergrad and learning devops, and i will like to document my journey here and share it with you through hashnode blogs.