Lettura del valore dalla console, in modo interattivo


155

Ho pensato di creare un semplice server http server con qualche estensione console. Ho trovato lo snippet da leggere dai dati della riga di comando.

  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();

  });

bene per porre le domande ripetutamente, non posso semplicemente usare il while(done) { }ciclo? Inoltre, se il server riceve l'output al momento della domanda, rovina la linea.


5
Presumo da parte rltua readline ?
jpaugh

Puoi usare un'interfaccia non bloccante come quella usata in questa risposta , quindi puoi fare un while(done)ciclo.
Keyvan

Risposte:


182

non puoi fare un ciclo "while (done)" perché ciò richiederebbe il blocco sull'input, cosa che a node.js non piace fare.

Invece imposta un callback da chiamare ogni volta che qualcosa viene inserito:

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() + "]");
  });

2
Grazie, funziona, l'ascoltatore "end" consente di chiamare alcune operazioni di chiusura e dire "Arrivederci"?
Risto Novik,

Ho rimosso l'ascoltatore "fine" dall'esempio, non so dove sarà davvero utile essere onesti.
rapina il

2
È possibile semplificare l'output della stringa in d.toString (). Trim ()
MKN Web Solutions

6
Questa risposta risale al 2011 e da allora molto è cambiato. In particolare, la prima parte della risposta, il non puoi fare un ciclo while ... non regge più. Sì, puoi avere un ciclo while e ancora non bloccare, grazie al modello async-waitit. Altre risposte lo riflettono. A chiunque legga questo al giorno d'oggi - consulta anche altre risposte.
Wiktor Zychla,

1
Per seguire su @WiktorZychla, la funzione process.openStdin mentre è ancora in funzione, è stata deprecata intorno al 2011 e non troverai alcuna documentazione a riguardo.
calder.ty,

112

Ho usato un'altra API per questo scopo ...

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);
});

Ciò consente di richiedere in loop fino a quando la risposta è right. Inoltre offre una bella console. Puoi trovare i dettagli @ http://nodejs.org/api/readline.html#readline_example_tiny_cli


11
Questa è un'ottima risposta Ciò che potrebbe non essere ovvio (ma è un grande vantaggio) è che readline non è dipendenza esterna: fa parte di node.js.
jlh

51

L'API Readline è cambiata un po 'da 12'. I documenti mostrano un esempio utile per catturare l'input dell'utente da un flusso standard:

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();
});

Maggiori informazioni qui


5
questo è solo un esempio di base. Come interagisci? domanda risposta? scelte multiple e simili? Come riaprire rl una volta chiuso, se non riesco a lavorare con open rl per interagire con l'utente incluso un po 'di logica
Pawel Cioch,

28

Credo che questo meriti un moderno async-await risposta , supponendo che venga utilizzato il nodo> = 7.x.

La risposta usa ancora ReadLine::questionma la avvolge in modo tale che while (done) {}sia possibile, cosa che l'OP chiede esplicitamente.

var cl = readln.createInterface( process.stdin, process.stdout );
var question = function(q) {
    return new Promise( (res, rej) => {
        cl.question( q, answer => {
            res(answer);
        })
    });
};

e quindi un esempio di utilizzo

(async function main() {
    var answer;
    while ( answer != 'yes' ) {
        answer = await question('Are you sure? ');
    }
    console.log( 'finally you are sure!');
})();

porta alla seguente conversazione

Are you sure? no
Are you sure? no
Are you sure? yes
finally you are sure!

Questa è esattamente la risposta che stavo cercando. Penso che dovrebbe essere il migliore.
William Chou,

Bellissimo. Async wait è necessario per script più grandi. Questo è esattamente ciò di cui avevo bisogno.
Abhay Shiro,

25

Si prega di utilizzare readline-sync , questo consente di lavorare con la console sincrona senza inferni di callback. Funziona anche con le password:

var favFood = read.question('What is your favorite food? ', {
  hideEchoBack: true // The typed text on screen is hidden by `*` (default). 
});


5
Ciò richiede ulteriore dipendenza, quindi preferirei altre soluzioni.
Risto Novik

Non funziona su SO "Uncaught ReferenceError: lettura non definita"
awwsmm

12

La risposta di @rob funzionerà la maggior parte delle volte, ma potrebbe non funzionare come previsto con input lunghi.

Questo è quello che dovresti usare invece:

const stdin = process.openStdin();
let content = '';

stdin.addListener('data', d => {
  content += d.toString();
});

stdin.addListener('end', () => {
  console.info(`Input: ${content}`);
});

Spiegazione del perché questa soluzione funziona:

addListener('data') funziona come un buffer, il callback verrà chiamato quando è pieno o / ed è la fine dell'input.

Che dire degli input lunghi? Un singolo'data' callback non sarà sufficiente, quindi otterrai il tuo input diviso in due o più parti. Questo spesso non è conveniente.

addListener('end')ci avviserà quando il lettore stdin avrà finito di leggere il nostro input. Dato che abbiamo archiviato i dati precedenti, ora possiamo leggerli ed elaborarli tutti insieme.


3
quando sto usando il codice sopra e inserisco alcuni input e poi il tasto "invio" la console continua a chiedermi ulteriori input. come dovremmo risolverlo?
Matan Tubul,

5

Consiglio di utilizzare Inquirer , poiché fornisce una raccolta di interfacce utente interattive comuni della riga di comando.

const inquirer = require('inquirer');

const questions = [{
  type: 'input',
  name: 'name',
  message: "What's your name?",
}];

const answers = await inquirer.prompt(questions);
console.log(answers);

5

Ecco un esempio:

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
})

Produzione:

Enter name: bob
Your name is: bob

Bella risposta fratello !! Semplice e chiaro.
MD.JULHAS HOSSAIN,

3

Questo è troppo complicato. Una versione più semplice di:

var rl = require('readline');
rl.createInterface... etc

sarebbe da usare

var rl = require('readline-sync');

quindi attenderà quando si utilizza

rl.question('string');

quindi è più facile da ripetere. per esempio:

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!');
}

2

Un caso d'uso comune sarebbe probabilmente l'app per visualizzare un prompt generico e gestirlo in un'istruzione switch.

È possibile ottenere un comportamento equivalente a un ciclo while utilizzando una funzione di supporto che si richiamerebbe nel 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;
    }
});

È possibile passare una stringa vuota anziché 'app> 'se l'app stampa già qualcosa sullo schermo al di fuori di questo ciclo.


2

Il mio approccio a questo sarebbe di usare generatori asincroni .

Supponendo di avere una serie di domande:

 const questions = [
        "How are you today ?",
        "What are you working on ?",
        "What do you think of async generators ?",
    ]

Per usare la awaitparola chiave, devi avvolgere il tuo programma in un IIFE asincrono.

(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)
})();

Risultati aspettati:

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' ]

Se vuoi ottenere domande e risposte del tutto, puoi farlo con una semplice modifica:

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' ] ]
   */

2

Ho dovuto scrivere un gioco "tic-tac-toe" in Node che ha preso input dalla riga di comando e ha scritto questo blocco di codice asincrono / attesa che ha funzionato.

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()

1

Blocco del comportamento sbloccato di readline

Immagina di avere tre domande a cui rispondere dalla console, dato che ora sai che questo codice non verrà eseguito perché il modulo standard readline ha un comportamento 'sbloccato', ad esempio ogni domanda rl. è un thread indipendente, quindi questo codice non verrà eseguito.

'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 );

Uscita corrente:

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)

La soluzione proposta utilizza un emettitore di eventi per segnalare la fine di un thread di sblocco e include la logica di loop e la fine del programma nella sua funzione di ascolto.

'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

Uscita corrente:

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' ] ]

0

Ho desiderato un piccolo script per leggere la directory e scrivere un nome di console nuovo file (esempio: 'name.txt') e testo nel 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();
  }
});

0

Il modo più semplice è usare readline-sync

Elabora uno per uno input e out put.

npm i readline-sync

per esempio:

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)
}

Dovresti davvero includere la tua requiredichiarazione. Non c'è motivo di lasciarlo fuori.
solidstatejake
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.