My Logo

PUBLISHED APRIL 28, 2024

Teghen Donald Ticha
Lastly Updated: 3 months ago
Reading time 17 mins

Reading Inputs from the Command Line in NodeJS

Learn the various ways and strategies to efficiently read command line inputs in nodeJS, making your program more interactive and flexible.
Reading Inputs from the Command Line in NodeJS

Prerequisite

Before diving in, it is recommended that, you have basic understanding of JavaScript and NodeJS fundamentals.

In addition, Familiarity with command line interface (CLI) usage is helpful but not required.

You may also benfit from reading the previous series part, which covers writing to the CLI in nodeJS

In the modern day software development, NodeJS has become a go-to platform for building server-side applications and command-line tools.

Command-line inputs, also known as command-line arguments or parameters, are vital for facilitating interaction with these applications directly from the terminal or command prompt.


When you execute a NodeJS script from the command line, you often need a way to pass data or instructions to the program. This is where command-line inputs come into play. They allow users to provide information or trigger specific actions without altering the source code.

NodeJS offers several methods for reading command-line inputs, each serving different purposes and scenarios. These methods include using process.argv, the Readline module, and third-party libraries like Commander.js and Inquirer.js.


Command-line inputs are essential for building versatile and interactive command-line interfaces, enabling developers to create powerful tools and applications that are accessible from the terminal.

In this guide, we'll explore various techniques for handling command-line inputs, providing insights into their usage, advantages, and best practices.

By the end, you'll have a comprehensive understanding of how to effectively read/accept command-line inputs in your nodeJS applications.

In nodeJS, accessing command-line inputs is facilitated by the process.argv array, which provides a straightforward way to retrieve the arguments passed to the node process when it was started.

Understanding how process.argv works and how to leverage it effectively is essential for handling command-line inputs in your nodeJS applications.

Understanding process.argv:

The process.argv array contains the command-line arguments passed to the node process. It's important to note that process.argv is an array where each element represents a command-line `argument`.

  • process.argv[0]: The first element contains the absolute path to the node executable.
  • process.argv[1]: The second element contains the path to the script being executed.
  • Subsequent elements (process.argv[2], process.argv[3], and so on) contain any additional arguments provided by the user when running the script.


For example, consider the following script.js file:

// script.js
console.log('Path to Node.js executable:', process.argv[0]);
console.log('Path to script being executed:', process.argv[1]);
console.log('Command-line arguments:', process.argv.slice(2));



Now let's run the script using the foloowing command:

node script.js Yohan 4months French/English/Meta

Breakdown:

  • process.argv[0] would be something like /usr/local/bin/node (the path to the NodeJS executable).
  • process.argv[1] would be the path to script.js.
  • process.argv[2], process.argv[3], and process.argv[4] would be "Yohan", "4months", and "French/English/Meta" respectively (the additional arguments provided by the user).



When you run the above with the command, the output would be:

Path to Node.js executable: /Users/donaldteghen/.nvm/versions/node/v20.11.1/bin/node
Path to script being executed: /Users/donaldteghen/Desktop/play/play.js
Command-line arguments: [ 'Yohan', '4months', 'French/English/Meta' ]

This demonstrates how process.argv can be used to access and process command-line arguments within a node script.

Pros and Cons:
  • Pros:
    • Built-in: process.argv is readily available without the need for external dependencies.
    • Lightweight: Since it's part of the Node.js runtime, there's minimal overhead.
    • Provides direct access to command-line arguments, making it suitable for simple use cases.
  • Cons:
    • Manual parsing: You need to manually parse the command-line arguments, which can be cumbersome for complex scenarios.
    • Limited functionality: process.argv is best suited for basic command-line arguments and lacks support for interactive prompts or structured input processing.


Now that we've explored the basics of using process.argv to handle command-line inputs in nodeJS, let's dive deeper into another essential method: the Readline module.

In nodeJS, the Readline module introduced in version 7, offers a powerful tool for interacting with users via the command line. Unlike process.argv, which handles basic command-line arguments, Readline allows for more sophisticated input processing and interactive prompts.

The Readline module facilitates reading input from a `Readable stream`, typically the command line, on a line-by-line basis.

It providing an interface for prompting users, receiving input, and processing responses in an asynchronous manner.

Basic Usage:

  1. 1.Create a new instance of readline interface using the createInterface method. This method take an option params. This option object contains lots of configuration option but we'll focus only on two:
    1. 1.input : The Readable stream to listen to. This option is required.
    2. 2.output : The Writable stream to write readline data to.
  2. 2.Once our interface is ready, we can start listening to line events , create a question/amswer prompt, etc...


As an example lets create a question/answer mini app, that promps questions, accepts answers and exit after printing a list of all questions and their correponding answers using the question method.

const readline = require('readline');

const question_bank = [
    'Where are you from ? ',
    'What is your name ?',
    'How old are you ?',
    'What do you do for fun ? ',
    'Do you enjoy coding ? ' ,
    'Why do you or don\'t you like coding ? '   
];
const answer_bank = [];

// create a readline interfcace
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    prompt: 'Next Question > '
});
  
rl.on('close', () => {
    console.log('\n***************** Question / Answer Summary *************** \n');
    question_bank.map(function (question, index) {
        console.log(`Q: ${question}\n A: ${answer_bank[index]}`);
    })
});

let index = 0;
function nextQuestion () {
    if (index >= question_bank.length) {
        return rl.close();
    }
    rl.question(question_bank[index], (answer) => {
        if (!answer || answer.length === 0) { // ask question until an answer is provided!
            return nextQuestion();
        }
        answer_bank[index] = answer;
        index++;
        nextQuestion();
    });
}
nextQuestion();

Breakdown:

  • process.stdin represents the standard input stream (the command line), and process.stdout represents the standard output stream (also the command line).
  • the quesions are presented to the user, and upon submitting their response, the callback function is executed with the provided answer.
  • The close method is then called to properly close the Readline interface when an aswer to the last question is received.

Let's have another example using the event API.

const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });
  
const lines = [];

rl.once('close', () => {
    console.log('******** Here are you inputs, line-by-line ***********');
    lines.forEach(line => {
        console.log(line);
    })
})
console.log('******** Start entering your text and use "return (⏎)" to move to the next line ***********');
rl.on('line', (input) => {  
    if (['end', 'quit', 'done'].includes(input)) {
        rl.close();
    }  
    lines.push(input);
});

Breakdown: In the above example, we prompt a user to enter, press enter or return to save the current statement and move to the next.
When the user is done, they can simple enter either done, quit or end to get all their statement and exit the prompt.


Clearing Lines and Cursors and closing readline Instance :

Readline provides methods for clearing lines and manipulating the cursor position on the command line:

  • rl.clearLine: Clears the current line of text.
  • rl.cursorTo: Moves the cursor to a specific column.
  • rl.close: Closes the rl instance and relinguishes control back to the input and output streams.

History and Tab Autocompletion: Readline supports features like input history and tab autocompletion, enhancing user experience and productivity. These features can be implemented using the appropriate configuration options and event handlers provided by the Readline module.

We have convered the basic and common use cases here, so for a more indepth description of each method, please visit the official docs.

Pros and Cons
  • Pros:
    • Interactivity: Readline enables interactive command-line interfaces, ideal for applications requiring user input.
    • Asynchronicity: User input is handled asynchronously, allowing concurrent execution of other tasks.
  • Cons:
    • Setup Overhead: Compared to process.argv, Readline requires additional setup.


Now that we've explored the various features and functionalities of the Readline module, including creating interfaces, listening for events, and manipulating the command line, let's shift our focus to third-party libraries that further enhance command-line interfaces in nodeJS

While node provides native modules like process.argv and Readline for handling command-line inputs, third-party libraries offer additional features and streamlined interfaces for building interactive command-line applications.

Two popular libraries in this domain are `Commander.js` and `Inquirer.js`.

Let's explore commander.js in detail and see how they can elevate your command-line interfaces.


Commander.js
:

Commander.js is a versatile framework for creating command-line interfaces (CLIs) in nodeJS.

It simplifies the process of defining commands, options, and arguments, while also generating help menus automatically.


Here's how you can use Commander.js:

const { program } = require('commander');

program
  .version('1.0.0')
  .description('CLI tool for MathLib library');

program
  .command('help')
  .description('Display help guide')
  .action(() => {
    console.log('=== MathLib CLI Help Guide ===');
    console.log('Usage: mathLibCLI [command]');
    console.log('');
    console.log('Commands:');
    console.log('  help\t\tDisplay help guide');
    console.log('  add <a,b,c....>\tAdd a series of comma separated numbers');
    console.log('  subtract a:b,c,d,e...\tSubtract a series of comma separated numbers from initial amount');
    console.log('  multiply <a,b,c...> \tMultiply series of comma separated numbers');
    console.log('  divide <a,b> \tDivide two numbers separated by a number ie 10,2 = 5');
  });

program.command('add')  
.argument('<string>', 'comma separated arguments to split')
.action(inputs => {    
    const sum = inputs.split(',').map(num => Number(num)).reduce((cur, total) => total + cur, total = 0)  ;
    console.log('sum is : ', sum);  
});

program.command('subtract')  
.argument('<string>', 'initial_amount:comma separated amounts to be subtracted')
.action(inputs => {    
    const [initialAmount, amounts_to_subtract] = inputs.split(':');
    const sub_sum =  amounts_to_subtract.split(',').map(num => Number(num)).reduce((cur, total) => total + cur, total = 0);
    const diff = Number(initialAmount) - sub_sum;
    console.log('Difference is : ', diff);  
});

program.command('multiply')  
.argument('<string>', 'comma separated arguments to split')
.action(inputs => {    
    const sum = inputs.split(',').map(num => Number(num)).reduce((cur, total) => total * cur, total = 1)  ;
    console.log('sum is : ', sum);  
});

program.command('divide')  
.argument('<string>', 'comma separated arguments to split ie [0] : numerator and [1]: denominator')
.action(inputs => {    
    const [numerator, denominator] = inputs.split(',').map(num => Number(num));
    if (denominator === 0) {
        throw new Error('Denominator can not be 0');
    }
    const result =  (numerator / denominator).toFixed(1);
    console.log('Result is : ', result);  
});

program.parse(process.argv);

Breakdown:

  • We define a Commander.js program with a version and description.
  • We add the following commands
    • `help`, that displays a custom help guide detailing the available commands and their usage.
    • `add`, that adds the arguements passed with the command
    • `subtract`, that subtracts the sum params from initial_amount
    • `multiple`, that multplies a set of inputs and
    • `divide` that divide two numbers
  • When you run node script.js help, you will see the help guide printed to the console.


Play around with above and if you're interested in more complex features and configurations, please visit the official documentation.

We'll not address requirer.js in this guide as, at the time of writing, it has been archived by it's owner. Nonetheless, if you're still interested in learning about it then, visit this repository.


By leveraging Commander.js or Inquirer.js, we can create a powerful command-line interface programs. These tools not only enhance the usability of our library but also make it accessible to a wider range of users, simplifying complex tasks with ease.

In conclusion, understanding command-line input handling empowers you as nodeJS developer to create versatile and user-friendly command-line applications.

Whether using native methods or third-party libraries, understanding these techniques enhances CLI development capabilities significantly.

All Chapter Parts for NodeJs In Theory, An absolute Beginner’s Overview
  1. Chapter 1 , Part 1 : Introduction to NodeJS

    In this series part, I introduce nodeJS and some technical concepts associated with it. I also show how easy it is to setup and start a simple nodeJS web server.

  2. Chapter 1 , Part 2 : How to Install and Setup NodeJS

    In this series part, I run you through the various ways to install nodeJS. I also discuss how to install nvm and use it to switch between different node versions.

  3. Chapter 1 , Part 3 : How much JavaScript do you need to learn NodeJS

    In this series part, we explore the nuanced relationship between JavaScript and NodeJS, highlighting some subtle distinctions between the two environments.

  4. Chapter 1 , Part 4 : The v8 Engine and the difference Between NodeJS and the browser

    In this series part, we explore the V8 engine and how it interacts with nodeJS. We also discuss node’s event loop and uncover the mystery behinds node’s ability to handle concurrent operations.

  5. Chapter 1 , Part 5 : NPM, the NodeJS package manager

    Discover the essentials of npm, the powerful package manager for Node.js. Learn installation, management, publishing, and best practices

  6. Chapter 1 , Part 6 : NodeJS in Development Vs Production

    Explore how Node.js behaves differently in development and production environments. Learn key considerations for deploying Node.js applications effectively.

  7. Chapter 2 , Part 1 : Asynchronous Flow Control

    In this series part, we'll explore various aspects of asynchronous flow control in Node.js, from basic concepts to advanced techniques.

  8. Chapter 2 , Part 2 : Blocking vs Non-blocking I/O

    Explore the differences between blocking and non-blocking I/O in Node.js, and learn how to optimize performance and scalability.

  9. Chapter 2 , Part 3 : Understanding NodeJS Event loop

    Exploring the Node.js event loop by understanding its phases, kernel integration, and processes enabling seamless handling of asynchronous operations in your applications.

  10. Chapter 2 , Part 4 : The NodeJS EventEmitter

    Explore the power of Node.js EventEmitter: an essential tool for building scalable and event-driven applications. Learn how to utilize it effectively!

  11. Chapter 3 , Part 1 : Working with files in NodeJS

    Gain comprehensive insights into file management in Node.js, covering file stats, paths, and descriptors, to streamline and enhance file operations in your applications.

  12. Chapter 3 , Part 2 : Reading and Writing Files in NodeJS

    Uncover the fundamentals of reading and writing files in nodeJS with comprehensive examples and use cases for some widely used methods.

  13. Chapter 3 , Part 3 : Working with Folders in NodeJS

    Unlock the secrets of folder manipulation in Node.js! Explore essential techniques and methods for working with directories efficiently

  14. Chapter 4 , Part 1 : Running NodeJS Scripts

    Master the command line interface for executing nodeJS scripts efficiently. Learn common options and best practices for seamless script execution

  15. Chapter 4 , Part 2 : Reading Environment Variables in NodeJS

    Learn how to efficiently manage environment variables in nodeJS applications. Explore various methods and best practices for security and portability

  16. Chapter 4 , Part 3 : Writing Outputs to the Command Line in NodeJS

    Learn essential techniques for writing outputs in nodeJS CLI. From basic logging to formatting and understanding stdout/stderr.

  17. Chapter 4 , Part 4 : Reading Inputs from the Command Line in NodeJS

    Learn the various ways and strategies to efficiently read command line inputs in nodeJS, making your program more interactive and flexible.

  18. Chapter 4 , Part 5 : The NodeJS Read, Evaluate, Print, and Loop (REPL)

    Explore the power of nodeJS's Read, Evaluate, Print, and Loop (REPL). Learn how to use this interactive environment for rapid prototyping, debugging, and experimentation.

  19. Chapter 5 , Part 1 : Introduction to Testing in NodeJS

    Discover the fundamentals of testing in nodeJS! Learn about testing types, frameworks, and best practices for building reliable applications.

  20. Chapter 5 , Part 2 : Debugging Tools and Techniques in NodeJS

    Explore essential debugging tools and techniques in Node.js development. From built-in options to advanced strategies, and best practices for effective debugging.

  21. Chapter 6 , Part 1 : Project Planning and Setup

    Discuss the planning and design process for building our interactive file explorer in Node.js, focusing on core features, UI/UX design, and implementation approach and initial setup.

  22. Chapter 6 , Part 2 : Implementing Basic functionalities

    In this guide, we'll implement the basic functionalities of our app which will cover initial welcome and action prompts.

  23. Chapter 6 , Part 3 : Implementating Core Features and Conclusion

    In this guide, we'll complete the rest of the more advanced functionalities of our app including, create, search, sort, delete, rename and navigate file directories.