【问题标题】:Reading value from console, interactively以交互方式从控制台读取值
【发布时间】:2011-12-29 00:55:56
【问题描述】:

我想用一些控制台扩展来制作一个简单的服务器 http 服务器。我发现 sn-p 可以从命令行数据中读取。

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

  });

重复提问,我不能简单地使用while(done) { } 循环吗?同样,如果服务器在提问时收到输出,它会破坏线路。

【问题讨论】:

  • 我假设rl 你的意思是readline
  • 你可以使用像this answer那样的非阻塞接口,然后你可以做一个while(done)循环。

标签: node.js console


【解决方案1】:

我相信这值得一个现代的async-await 答案,假设使用 node >= 7.x。

答案仍然使用ReadLine::question,但将其包装起来以便while (done) {} 成为可能,这是OP 明确询问的问题。

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

然后是一个示例用法

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

导致以下对话

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

编辑。为了正确结束输入,调用

cl.close();

在脚本的末尾。

【讨论】:

  • 这正是我正在寻找的答案。我觉得应该是第一名。
  • 美丽。较大的脚本需要异步等待。这正是我所需要的。
  • 简洁。但输入总是停止,不会结束。即使按回车,控制台也卡住了。按 ctrl-c 或 ctrl-d 使其跳出。缺少什么?
  • @MarcusWiderberg 在脚本末尾添加cl.close()。问候。
【解决方案2】:

最简单的方法是使用 readline-sync

它会一一处理输入和输出。

npm i readline-sync

例如:

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

【讨论】:

  • 你真的应该包括你的require声明。没有理由忽略它。
【解决方案3】:

我必须在 Node 中编写一个“井字游戏”,该游戏从命令行获取输入,并编写了这个基本的异步/等待代码块来解决问题。

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

【讨论】:

    【解决方案4】:

    这太复杂了。更简单的版本:

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

    会用

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

    然后它会在你使用时等待

    rl.question('string');
    

    然后更容易重复。 例如:

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

    【讨论】:

      【解决方案5】:

      我为读取目录创建了一个小脚本,并将控制台名称新文件(例如:'name.txt')和文本写入文件。

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

      【讨论】:

        【解决方案6】:

        我的方法是使用异步生成器

        假设您有一系列问题:

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

        为了使用 await 关键字,您必须将程序包装到异步 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)
        })();
        

        预期结果:

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

        如果您想完全获得问题和答案,您可以通过简单的修改来实现:

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

        【讨论】:

          【解决方案7】:

          这是一个例子:

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

          输出:

          Enter name: bob
          Your name is: bob
          

          【讨论】:

          • 好回答兄弟!!简单明了。
          【解决方案8】:

          阻止 readline 未阻止的行为

          假设您需要从控制台回答三个问题,因为您现在知道此代码不会运行,因为 readline 标准模块具有“未阻塞”行为,即每个 rl.question 都是独立线程,因此此代码不会运行。

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

          运行输出:

          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)
          

          建议的解决方案使用事件发射器来发出解除阻塞线程结束的信号,并将循环逻辑和程序结束包含在其侦听器函数中。

          '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
          

          运行输出:

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

          【讨论】:

            【解决方案9】:

            我推荐使用Inquirer,因为它提供了一组常用的交互式命令行用户界面。

            const inquirer = require('inquirer');
            
            const questions = [{
              type: 'input',
              name: 'name',
              message: "What's your name?",
            }];
            
            const answers = await inquirer.prompt(questions);
            console.log(answers);
            

            【讨论】:

              【解决方案10】:

              一个常见的用例可能是应用程序显示一个通用提示并在 switch 语句中处理它。

              您可以通过使用会在回调中调用自身的辅助函数来获得与 while 循环等效的行为:

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

              如果您的应用已经在此循环之外在屏幕上打印了一些内容,您可以传递一个空字符串而不是 'app&gt; '

              【讨论】:

                【解决方案11】:

                请使用readline-sync,这让您可以使用同步控制台而无需回调地狱。甚至可以使用密码:

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

                【讨论】:

                • 这需要额外的依赖,所以我更喜欢其他解决方案。
                • 不运行在 SO "Uncaught ReferenceError: read is not defined"
                【解决方案12】:

                @rob answer 在大多数情况下都可以正常工作,但对于长输入,它可能无法按预期工作。

                这就是你应该改用的:

                const stdin = process.openStdin();
                let content = '';
                
                stdin.addListener('data', d => {
                  content += d.toString();
                });
                
                stdin.addListener('end', () => {
                  console.info(`Input: ${content}`);
                });
                

                解释为什么这个解决方案有效:

                addListener('data') 像缓冲区一样工作,当它满了或/和输入结束时将调用回调。

                长输入呢?单个'data' 回调是不够的,因此您会将输入分成两部分或更多部分。这通常不方便。

                addListener('end') 将在标准输入读取器完成读取我们的输入时通知我们。由于我们一直在存储以前的数据,因此我们现在可以一起读取和处理它们。

                【讨论】:

                • 当我使用上面的代码并插入一些输入然后“输入”键时,控制台不断要求我提供更多输入。我们应该如何终止它?
                【解决方案13】:

                自 12 年以来,Readline API 发生了很大变化。该文档显示了一个从标准流中捕获用户输入的有用示例:

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

                More information here.

                【讨论】:

                • 这只是一个基本的例子。你们如何互动?问题/答案?多项选择之类的?如何在关闭后重新打开 rl,如果不能使用 open rl 与用户交互,包括一些逻辑
                • 如果我们想在另一个函数中使用,你将如何存储答案的值?
                【解决方案14】:

                我为此使用了另一个 API..

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

                这允许循环提示,直到答案为right。它还提供了不错的小控制台。您可以找到详细信息@http://nodejs.org/api/readline.html#readline_example_tiny_cli

                【讨论】:

                • 这是一个很好的答案。可能不明显(但有很大优势)的是 readline 不是外部依赖:它是 node.js 的一部分。
                • 我得到 Uncaught ReferenceError: "Whatever I write" is not defined.
                【解决方案15】:

                您不能执行“while(done)”循环,因为这需要阻塞输入,这是 node.js 不喜欢做的事情。

                而是设置每次输入内容时调用的回调:

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

                【讨论】:

                • 谢谢你这个作品,“结束”监听器是否允许调用一些关闭操作并说“再见”?
                • 我从示例中删除了“结束”侦听器,我不知道说实话在哪里真正有用。
                • 可以将字符串输出简化为d.toString().trim()
                • 这个答案日期为 2011 年,此后发生了很大变化。特别是,答案的第一部分,you can't do a while loop ... 不再成立。是的,由于 async-await 模式,您可以有一个 while 循环并且仍然不会阻塞。其他答案反映了这一点。对于现在阅读本文的任何人 - 请同时查阅其他答案。
                • 继续@WiktorZychla,仍在工作的函数 process.openStdin 在 2011 年左右被弃用,您将找不到任何有关它的文档。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2012-06-24
                • 2020-05-11
                • 2014-10-21
                • 2013-09-28
                • 2012-06-23
                • 2023-03-30
                • 2015-01-20
                相关资源
                最近更新 更多