How to read variable number of lines from the terminal? - node.js

I need to ask how many inputs the user want to give (one input per line) and read them.
I am trying to keep it as simple as possible without using any libraries or even async. Why? Because I am doing a course on Coursera and I have to upload a JavaScript/Node file as assignment. The one I wrote with async and promise got rejected (for that version look at the end of this post). Somehow input and output is not working with the "Coursera system".
So I am trying to do it without any bells and whistles:
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
terminal: false
});
const r2 = readline.createInterface({
input: process.stdin,
terminal: false
});
let x = null;
let y = [];
rl.on('line', line1 => {
if (line1 !== "\n") {
x = parseInt(line1);
while(x > 0){
r2.on('line', line2 => {
y.push(line2);
});
x -= 1;
}
console.log('You entered these colors:', y);
process.exit();
}
});
Output:
3
You entered these colors: []
Expected output:
[input]3
[input]green
[input]blue
[input]red
[output]You entered these colors: ["green", "blue", "red"]
One that worked but rejected buy Coursera "assignment system":
const readline = require('readline');
const readLineAsync = () => {
const rl = readline.createInterface({
input: process.stdin,
terminal: false
});
return new Promise((resolve) => {
rl.prompt();
rl.on('line', (line) => {
rl.close();
resolve(line);
});
});
};
async function start() {
let line = null;
let colors = [];
line = await readLineAsync();
const numColors = parseInt(line);
for(let count = numColors; count > 0; count--) {
line = await readLineAsync();
colors.push(line);
}
console.log('You entered these colors:', colors);
}
start();

Initially, we want to ask how many colors the user intends to enter. We are sure the input is expected only once so r1.once() is used.
But, the number of times we ask the user for input varies according to the previous input. So we use r1.on() which will not close until terminated. We will use a variable, counter, to ensure the input is asked of the user the required number of times.
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
terminal: false
});
process.stdin.setEncoding('utf8');
rl.once('line', line => {
const count = parseInt(line.toString());
const colors = [];
let counter = 0;
rl.on('line', line => {
const color = readLine(line);
colors.push(color);
if(++counter >= count) {
console.log('You have entered these colros:', colors);
process.exit();
}
});
});
function readLine(line) {
return line.toString();
}
output
3
red
green
blue
You have entered these colros: [ 'red', 'green', 'blue' ]

Related

Nodejs multiple inputs from console - mix of masked and unmasked outputs

I am trying to create a small nodejs utility to produce and consume kafka messages using kafkajs. I want to get the kafka user and password input from the console. I tried different combinations to use the stdoutMuted to true and false at various places, but it doesn't work correctly. If it is set to true before the question is asked then I either get undefined or the question doesn't show up
Using some examples on SO, similar to this and this, I tried to create something like this
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl._writeToOutput = stringToWrite => {
if (rl.stdoutMuted)
rl.output.write("*");
else
rl.output.write(stringToWrite);
};
rl.stdoutMuted = true;
rl.question('Password: ', password => {
rl.stdoutMuted = false;
console.log('\nPassword is ' + password);
rl.close();
});
This gives below
* *******
Password is mypass
OR
'use strict'
const readline = require('readline')
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
const question1 = () => {
return new Promise((resolve, reject) => {
rl.stdoutMuted = false;
rl.question('Userid : ', (answer) => {
console.log(`Thank you for your valuable feedback: ${answer}`)
resolve()
})
})
}
const question2 = () => {
return new Promise((resolve, reject) => {
rl.stdoutMuted = true;
rl.question('Password : ', (answer) => {
console.log(`Thank you for your valuable feedback: ${answer}`)
resolve()
})
})
}
rl._writeToOutput = function _writeToOutput(stringToWrite) {
if (rl.stdoutMuted)
rl.output.write("\x1B[2K\x1B[200D"+rl.query+"["+((rl.line.length%2==1)?"=-":"-=")+"]");
else
rl.output.write(stringToWrite);
};
const main = async () => {
await question1()
await question2()
rl.close()
}
main()
Gives the below
UserId user
Thank you for your valuable feedback: user
undefined[-=]Thank you for your valuable feedback: password
How can I setup the input such that I can request multiple inputs where some of them will be masked and others plaintext, but all the questions / prompts are visible.
Also, how do I set those as variables that can be used in other parts of the application to connect to Kafka.
Thank you

Node.js readline property doesn't ask for input

Node.js readline property doesn't stop for input, instead continues program, causing app to crash. While trying to solve this I found out that apparently node does the whole code simultaneously and doesn't because of that stop for input. I found out ways to run this code but they didn't work for me.
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
var token;
var pass;
rl.question('token: ', (tok) => {
token = tok;
rl.close();
});
rl.question('pass: ', (pas) => {
pass = pas;
rl.close();
});
What can I do to solve this?
I hope you are looking something like below:
const readLine = require('readline');
const util = require('util')
const { promisify } = util;
const readline = readLine.createInterface({
input: process.stdin,
output: process.stdout,
});
// Prepare readline.question for promisification
readline.question[util.promisify.custom] = (question) => {
return new Promise((resolve) => {
readline.question(question, resolve);
});
};
let questionPs = promisify(readline.question);
async function askQuestions (questions,readline) {
let answers= [];
for(let i=0;i<2;i++){
let tmp = await questionPs(q[i]);
answers.push(tmp)
}
console.log(answers);
readline.close();
}
// Usage example:
let q = ['token:','pass:']
askQuestions(q,readline)

Prevent empty lines from output in node readline

When I press and hold ENTER on my keyboard, the readline module will then output empty lines to my writeable stream(in this case it is process.stdout).
But since I don't want empty lines to clutter my console I want to prevent it somehow from being outputted.
Can I use something to filter out empty lines in my stream? Maybe using a transform stream of some sorts?
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
removeHistoryDuplicates: true
});
rl.on("line", (line) => {
if(line.trim().length === 0) {
// don't output anything, but it is already too late because it is already written to stream :'-(
}
rl.prompt();
})
use this
readline.cursorTo(process.stdout, 0,0);
enter will automatically be ignored and return to the begining
https://nodejs.org/api/readline.html#readline_readline_clearline_stream_dir
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
removeHistoryDuplicates: true
});
const enterPressed=(line)=>{
return line.replace(/[^a-zA-Z]/g, "").trim().length === 0
}
rl.on("line", (line) => {
if(enterPressed(line)) {
readline.cursorTo(process.stdout, 0,0);
}
rl.prompt();
})

How to get synchronous readline, or "simulate" it using async, in nodejs?

I am wondering if there is a simple way to get "synchronous" readline or at least get the appearance of synchronous I/O in node.js
I use something like this but it is quite awkward
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
var i = 0;
var s1 = '';
var s2 = '';
rl.on('line', function(line){
if(i==0) { s1 = line; }
else if(i==1) { s2 = line; }
i++;
})
rl.on('close', function() {
//do something with lines
})'
Instead of this I would prefer if it were as easy as something like
var s1 = getline(); // or "await getline()?"
var s2 = getline(); // or "await getline()?"
Helpful conditions:
(a) Prefer not using external modules or /dev/stdio filehandle, I am submitting code to a code submission website and these do not work there
(b) Can use async/await or generators
(c) Should be line based
(d) Should not require reading entire stdin into memory before processing
Just in case someone stumbles upon here in future
Node 11.7 added support for this using async await
const readline = require('readline');
//const fileStream = fs.createReadStream('input.txt');
const rl = readline.createInterface({
input: process.stdin, //or fileStream
output: process.stdout
});
for await (const line of rl) {
console.log(line)
}
Remember to wrap it in async function(){} otherwise you will get a reserved_keyword_error.
const start = async () =>{
for await (const line of rl) {
console.log(line)
}
}
start()
To read an individual line, you can use the async iterator manually
const it = rl[Symbol.asyncIterator]();
const line1 = await it.next();
You can just wrap it in a promise -
const answer = await new Promise(resolve => {
rl.question("What is your name? ", resolve)
})
console.log(answer)
Like readline module, there is another module called readline-sync, which takes synchronous input.
Example:
const reader = require("readline-sync"); //npm install readline-sync
let username = reader.question("Username: ");
const password = reader.question("Password: ",{ hideEchoBack: true });
if (username == "admin" && password == "foobar") {
console.log("Welcome!")
}
I think this is what you want :
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin , output: process.stdout });
const getLine = (function () {
const getLineGen = (async function* () {
for await (const line of rl) {
yield line;
}
})();
return async () => ((await getLineGen.next()).value);
})();
const main = async () => {
let a = Number(await getLine());
let b = Number(await getLine());
console.log(a+b);
process.exit(0);
};
main();
Note: this answer use experimental features and need Node v11.7
Try this. It's still not a perfect replication of a synchronous line reading function -- e.g. async functions still happen later, so some of your calling code may execute out of order, and you can't call it from inside a normal for loop -- but it's a lot easier to read than the typical .on or .question code.
// standard 'readline' boilerplate
const readline = require('readline');
const readlineInterface = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// new function that promises to ask a question and
// resolve to its answer
function ask(questionText) {
return new Promise((resolve, reject) => {
readlineInterface.question(questionText, (input) => resolve(input) );
});
}
// launch your program since `await` only works inside `async` functions
start()
// use promise-based `ask` function to ask several questions
// in a row and assign each answer to a variable
async function start() {
console.log()
let name = await ask("what is your name? ")
let quest = await ask("what is your quest? ")
let color = await ask("what is your favorite color? ")
console.log("Hello " + name + "! " +
"Good luck with " + quest +
"and here is a " + color + " flower for you.");
process.exit()
}
UPDATE: https://www.npmjs.com/package/readline-promise implements it (source code here: https://github.com/bhoriuchi/readline-promise/blob/master/src/index.js#L192 ). It implements several other features as well, but they seem useful too, and not too overengineered, unlike some other NPM packages that purport to do the same thing. Unfortunately, I can't get it to work due to https://github.com/bhoriuchi/readline-promise/issues/5 but I like its implementation of the central function:
function ask(questionText) {
return new Promise((resolve, reject) => {
readlineInterface.question(questionText, resolve);
});
}
Using generators your example would look like this:
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
var i = 0;
var s1 = '';
var s2 = '';
var iter=(function* () {
s1 = yield;
i++;
s2 = yield;
i++;
while (true) {
yield;
i++;
}
})(); iter.next();
rl.on('line', line=>iter.next(line))
rl.on('close', function() {
//do something with lines
})
So yield here acts as if it were a blocking getline() and you can handle lines in the usual sequential fashion.
UPD:
And an async/await version might look like the following:
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
var i = 0;
var s1 = '';
var s2 = '';
var continuation;
var getline = (() => {
var thenable = {
then: resolve => {
continuation = resolve;
}
};
return ()=>thenable;
})();
(async function() {
s1 = await getline();
i++;
s2 = await getline();
i++;
while (true) {
await getline();
i++;
}
})();
rl.on('line', line=>continuation(line))
rl.on('close', function() {
//do something with lines
})
In both of these "synchronous" versions, i is not used for distinguishing lines and only useful for counting the total number of them.
Here's an example but it requires reading entire stdin before giving results however which is not ideal
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
function lineiterator() {
var currLine = 0;
var lines = [];
return new Promise(function(resolve, reject) {
rl.on('line', function (line){
lines.push(line)
})
rl.on('close', function () {
resolve({
next: function() {
return currLine < lines.length ? lines[currLine++]: null;
}
});
})
})
}
Example
lineiterator().then(function(x) {
console.log(x.next())
console.log(x.next())
})
$ echo test$\ntest | node test.js
test
test
The simplest (and preferred) option is available in the docs. https://nodejs.org/api/readline.html#rlquestionquery-options-callback
const util = require('util');
const question = util.promisify(rl.question).bind(rl);
async function questionExample() {
try {
const answer = await question('What is you favorite food? ');
console.log(`Oh, so your favorite food is ${answer}`);
} catch (err) {
console.error('Question rejected', err);
}
}
questionExample();
Since I don't know how many strings you need I put them all in an Array
Don't hesitate to comment if you need a more detailed answer or if my answer is not exact :
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
var i = 0;
var strings = [];
rl.on('line', function(line) {
// 2 lines below are in case you want to stop the interface after 10 lines
// if (i == 9)
// rl.close()
strings[i] = line
i++
}).on('close', function() {
console.log(strings)
})
// this is in case you want to stop the program when you type ctrl + C
process.on('SIGINT', function() {
rl.close()
})
We can use promise and process.stdin events together to simulate a synchronous input system
const { EOL } = require("os");
const getLine = async () =>
(
await new Promise((resolve) => {
process.stdin.on("data", (line) => {
resolve("" + line);
});
})
).split(EOL)[0];
const line = await getLine();
console.log(line);

How to read user input in node.js for simple calculations

How can I read user input from the command line in node.js for simple calculations? I'v been reading http://nodejs.org/api/readline.html#readline_readline and http://nodejs.org/api/process.html#process_process_stdin but I can't use my input for simple things like console.log(input). I know these are async functions, but I guess there must be a way of using the input for later calculations.
Do you have an example? Like a sum of two given numbers: input a and b and output a+b
Something like this?
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
function processSum(number) {
// Insert code to do whatever with sum here.
console.log('The sum is', number);
}
rl.question('Enter a number: ', function (x) {
rl.question('Enter another number: ', function (y) {
var sum = parseFloat(x) + parseFloat(y);
processSum(sum)
rl.close();
});
});
You could write a reusable module like this:
// ask.js
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.pause();
function ask(question, cb = () => void 0) {
return new Promise(resolve => {
rl.question(question, (...args) => {
rl.pause();
resolve(...args);
cb(...args);
});
});
}
module.exports = ask;
And use it with multiple approaches everywhere:
Approach #1 (with async/await):
const ask = require("./ask");
(async () => {
const a = await ask("Enter the first number: ");
const b = await ask("Enter the second number: ");
console.log("The sum is", a + b);
})();
Approach #2 (with Promise):
const ask = require("./ask");
ask("Enter the first number: ")
.then(a => {
ask("Enter the second number: ")
.then(b => {
console.log("The sum is", a + b);
});
});
Approach #3 (with callback):
const ask = require("./ask");
ask("Enter the first number: ", a => {
ask("Enter the second number: ", b => {
console.log("The sum is ", a + b);
});
});

Resources