Reputation: 8295
I thought to make an simple server http server with some console extension. I found the snippet to read from command line data.
var i = rl.createInterface(process.stdin, process.stdout, null);
i.question('Write your name: ', function(answer) {
console.log('Nice to meet you> ' + answer);
i.close();
process.stdin.destroy();
});
well to ask the questions repeatedly, i cant simply use the while(done) { }
loop? Also well if the server receives output at the question time, it ruins the line.
Upvotes: 172
Views: 228730
Reputation: 436
### Building on Rob's answer from 2011
a #CoffeeScript way to do the listener. ###
standin = process.stdin
out = process.stdout
standin.setEncoding('ascii')
# no data values returned inside the function
# are available outside the function.
# So running entire program from listener.
standin.addListener('data', (buffer) ->
arrayIn = buffer.toString().split('\n')
t = lineIn()
console.log("lineIn=" + lineIn() + " line #=" + lineNum
+ " arrayIn=[" + arrayIn + "]")
)
Upvotes: 0
Reputation: 48314
I believe this deserves a modern async-await
answer, assuming node >= 7.x is used.
The answer still uses ReadLine::question
but wraps it so that the while (done) {}
is possible, which is something the OP asks about explicitely.
var cl = readln.createInterface( process.stdin, process.stdout );
var question = function(q) {
return new Promise( (res, rej) => {
cl.question( q, answer => {
res(answer);
})
});
};
and then an example usage
(async function main() {
var answer;
while ( answer != 'yes' ) {
answer = await question('Are you sure? ');
}
console.log( 'finally you are sure!');
})();
leads to following conversation
Are you sure? no
Are you sure? no
Are you sure? yes
finally you are sure!
Edit. In order to properly end the input, call
cl.close();
at the end of the script.
Upvotes: 45
Reputation: 4076
Easiest way is to use readline-sync
It process one by one input and out put.
npm i readline-sync
eg:
var firstPrompt = readlineSync.question('Are you sure want to initialize new db? This will drop whole database and create new one, Enter: (yes/no) ');
if (firstPrompt === 'yes') {
console.log('--firstPrompt--', firstPrompt)
startProcess()
} else if (firstPrompt === 'no') {
var secondPrompt = readlineSync.question('Do you want to modify migration?, Enter: (yes/no) ');
console.log('secondPrompt ', secondPrompt)
startAnother()
} else {
console.log('Invalid Input')
process.exit(0)
}
Upvotes: 1
Reputation: 1511
I had to write a "tic-tac-toe" game in Node that took input from the command line, and wrote this basic async/await block of code that did the trick.
const readline = require('readline')
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
async function getAnswer (prompt) {
const answer = await new Promise((resolve, reject) =>{
rl.question(`${prompt}\n`, (answer) => {
resolve(answer)
});
})
return answer
}
let done = false
const playGame = async () => {
let i = 1
let prompt = `Question #${i}, enter "q" to quit`
while (!done) {
i += 1
const answer = await getAnswer(prompt)
console.log(`${answer}`)
prompt = processAnswer(answer, i)
}
rl.close()
}
const processAnswer = (answer, i) => {
// this will be set depending on the answer
let prompt = `Question #${i}, enter "q" to quit`
// if answer === 'q', then quit
if (answer === 'q') {
console.log('User entered q to quit')
done = true
return
}
// parse answer
// if answer is invalid, return new prompt to reenter
// if answer is valid, process next move
// create next prompt
return prompt
}
playGame()
Upvotes: 2
Reputation: 31
This is overcomplicated. An easier version of:
var rl = require('readline');
rl.createInterface... etc
would be to use
var rl = require('readline-sync');
then it will wait when you use
rl.question('string');
then it is easier to repeat. for example:
var rl = require('readline-sync');
for(let i=0;i<10;i++) {
var ans = rl.question('What\'s your favourite food?');
console.log('I like '+ans+' too!');
}
Upvotes: 3
Reputation: 102
I have craeted a little script for read directory and write a console name new file (example: 'name.txt' ) and text into file.
const readline = require('readline');
const fs = require('fs');
const pathFile = fs.readdirSync('.');
const file = readline.createInterface({
input: process.stdin,
output: process.stdout
});
file.question('Insert name of your file? ', (f) => {
console.log('File is: ',f.toString().trim());
try{
file.question('Insert text of your file? ', (d) => {
console.log('Text is: ',d.toString().trim());
try {
if(f != ''){
if (fs.existsSync(f)) {
//file exists
console.log('file exist');
return file.close();
}else{
//save file
fs.writeFile(f, d, (err) => {
if (err) throw err;
console.log('The file has been saved!');
file.close();
});
}
}else{
//file empty
console.log('Not file is created!');
console.log(pathFile);
file.close();
}
} catch(err) {
console.error(err);
file.close();
}
});
}catch(err){
console.log(err);
file.close();
}
});
Upvotes: 0
Reputation: 11979
My approach to this would be to use async generators.
Assuming you have an array of questions:
const questions = [
"How are you today ?",
"What are you working on ?",
"What do you think of async generators ?",
]
In order to use the await
keyword, you must wrap your program into an async IIFE.
(async () => {
questions[Symbol.asyncIterator] = async function * () {
const stdin = process.openStdin()
for (const q of this) {
// The promise won't be solved until you type something
const res = await new Promise((resolve, reject) => {
console.log(q)
stdin.addListener('data', data => {
resolve(data.toString())
reject('err')
});
})
yield [q, res];
}
};
for await (const res of questions) {
console.log(res)
}
process.exit(0)
})();
Expected results:
How are you today ?
good
[ 'How are you today ?', 'good\n' ]
What are you working on ?
:)
[ 'What are you working on ?', ':)\n' ]
What do you think about async generators ?
awesome
[ 'What do you think about async generators ?', 'awesome\n' ]
If you want to get questions an answers altogether, you can achieve this with a simple modification:
const questionsAndAnswers = [];
for await (const res of questions) {
// console.log(res)
questionsAndAnswers.push(res)
}
console.log(questionsAndAnswers)
/*
[ [ 'How are you today ?', 'good\n' ],
[ 'What are you working on ?', ':)\n' ],
[ 'What do you think about async generators ?', 'awesome\n' ] ]
*/
Upvotes: 2
Reputation: 20633
Here's a example:
const stdin = process.openStdin()
process.stdout.write('Enter name: ')
stdin.addListener('data', text => {
const name = text.toString().trim()
console.log('Your name is: ' + name)
stdin.pause() // stop reading
})
Output:
Enter name: bob
Your name is: bob
Upvotes: 10
Reputation: 91
Blocking readline unblocked behaviour
Imagine you have three questions to be answered from the console, as you now know this code will not run because readline standard module has 'unblocked' behaviour say each rl.question is an independent thread so this code will not run.
'use strict';
var questionaire=[['First Question: ',''],['Second Question: ',''],['Third Question: ','']];
function askaquestion(question) {
const readline = require('readline');
const rl = readline.createInterface(
{input: process.stdin, output:process.stdout}
);
rl.question(question[0], function(answer) {
console.log(answer);
question[1] = answer;
rl.close();
});
};
var i=0;
for (i=0; i < questionaire.length; i++) {
askaquestion(questionaire[i]);
}
console.log('Results:',questionaire );
Running output:
node test.js
Third Question: Results: [ [ 'First Question: ', '' ],
[ 'Second Question: ', '' ],
[ 'Third Question: ', '' ] ] <--- the last question remain unoverwritten and then the final line of the program is shown as the threads were running waiting for answers (see below)
aaa <--- I responded with a single 'a' that was sweeped by 3 running threads
a <--- Response of one thread
a <--- Response of another thread
a <--- Response of another thread (there is no order on threads exit)
The proposed solution uses an event emitter to signal the end of an unblocking thread, and includes the loop logic and end of program into its listener function.
'use strict';
var questionaire=[['First Question: ',''],['Second Question: ',''],['Third Question: ','']];
// Introduce EventEmitter object
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {};
const myEmitter = new MyEmitter();
myEmitter.on('continue', () => {
console.log('continue...');
i++; if (i< questionaire.length) askaquestion(questionaire[i],myEmitter); // add here relevant loop logic
else console.log('end of loop!\nResults:',questionaire );
});
//
function askaquestion(p_question,p_my_Emitter) { // add a parameter to include my_Emitter
const readline = require('readline');
const rl = readline.createInterface(
{input: process.stdin, output:process.stdout}
);
rl.question(p_question[0], function(answer) {
console.log(answer);
p_question[1] = answer;
rl.close();
myEmitter.emit('continue'); // Emit 'continue' event after the question was responded (detect end of unblocking thread)
});
};
/*var i=0;
for (i=0; i < questionaire.length; i++) {
askaquestion(questionaire[i],myEmitter);
}*/
var i=0;
askaquestion(questionaire[0],myEmitter); // entry point to the blocking loop
// console.log('Results:',questionaire ) <- moved to the truly end of the program
Running output:
node test2.js
First Question: 1
1
continue...
Second Question: 2
2
continue...
Third Question: 3
3
continue...
done!
Results: [ [ 'First Question: ', '1' ],
[ 'Second Question: ', '2' ],
[ 'Third Question: ', '3' ] ]
Upvotes: 1
Reputation: 22277
I recommend using Inquirer, since it provides a collection of common interactive command line user interfaces.
const inquirer = require('inquirer');
const questions = [{
type: 'input',
name: 'name',
message: "What's your name?",
}];
const answers = await inquirer.prompt(questions);
console.log(answers);
Upvotes: 5
Reputation: 2956
A common use case would probably be for the app to display a generic prompt and handle it in a switch statement.
You could get a behaviour equivalent to a while loop by using a helper function that would call itself in the callback:
const readline = require('readline');
const rl = readline.createInterface(process.stdin, process.stdout);
function promptInput (prompt, handler)
{
rl.question(prompt, input =>
{
if (handler(input) !== false)
{
promptInput(prompt, handler);
}
else
{
rl.close();
}
});
}
promptInput('app> ', input =>
{
switch (input)
{
case 'my command':
// handle this command
break;
case 'exit':
console.log('Bye!');
return false;
}
});
You could pass an empty string instead of 'app> '
if your app already prints something to the screen outside of this loop.
Upvotes: 2
Reputation: 255
Please use readline-sync, this lets you working with synchronous console withouts callbacks hells. Even works with passwords:
var favFood = read.question('What is your favorite food? ', {
hideEchoBack: true // The typed text on screen is hidden by `*` (default).
});
Upvotes: 23
Reputation: 32807
@rob answer will work most of the times, but it might not work as you expect with long inputs.
That is what you should be using instead:
const stdin = process.openStdin();
let content = '';
stdin.addListener('data', d => {
content += d.toString();
});
stdin.addListener('end', () => {
console.info(`Input: ${content}`);
});
Explanation on why this solution works:
addListener('data')
works like a buffer, callback will be called when it is full or/and its the end of input.
What about long inputs? A single 'data'
callback will not be enough, hence it you will get your input split in two or more parts. That is often not convenient.
addListener('end')
will notify us when the stdin reader is done reading our input. Since we have been storing the previous data, we can now read and process it all together.
Upvotes: 12
Reputation: 4564
The Readline API has changed quite a bit since 12'. The doc's show a useful example to capture user input from a standard stream :
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('What do you think of Node.js? ', (answer) => {
console.log('Thank you for your valuable feedback:', answer);
rl.close();
});
Upvotes: 55
Reputation: 2303
I've used another API for this purpose..
var readline = require('readline');
var rl = readline.createInterface(process.stdin, process.stdout);
rl.setPrompt('guess> ');
rl.prompt();
rl.on('line', function(line) {
if (line === "right") rl.close();
rl.prompt();
}).on('close',function(){
process.exit(0);
});
This allows to prompt in loop until the answer is right
. Also it gives nice little console.You can find the details @ http://nodejs.org/api/readline.html#readline_example_tiny_cli
Upvotes: 122
Reputation: 10117
you can't do a "while(done)" loop because that would require blocking on input, something node.js doesn't like to do.
Instead set up a callback to be called each time something is entered:
var stdin = process.openStdin();
stdin.addListener("data", function(d) {
// note: d is an object, and when converted to a string it will
// end with a linefeed. so we (rather crudely) account for that
// with toString() and then trim()
console.log("you entered: [" +
d.toString().trim() + "]");
});
Upvotes: 195