PUBLISHED APRIL 28, 2024
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.
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 toscript.js
. - ➢
process.argv[2]
,process.argv[3]
, andprocess.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.
- ➢Built-in:
- ➢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.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.
input
: The Readable stream to listen to. This option is required. - 2.
output
: The Writable stream to write readline data to.
- 1.
- 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), andprocess.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 theinput
andoutput
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.
- ➢Setup Overhead: Compared to
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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!
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.
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.
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
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
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
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.
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.
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.
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.
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.
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.
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.
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.