Đọc giá trị từ bảng điều khiển, tương tác


155

Tôi nghĩ để tạo một máy chủ http máy chủ đơn giản với một số phần mở rộng giao diện điều khiển. Tôi tìm thấy đoạn mã để đọc từ dữ liệu dòng lệnh.

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

  });

tốt để đặt câu hỏi nhiều lần, tôi không thể sử dụng while(done) { }vòng lặp? Cũng nếu máy chủ nhận được đầu ra tại thời điểm câu hỏi, nó sẽ phá hỏng dòng.


5
Tôi giả định bởi rlbạn bình readline ?
jpaugh

Bạn có thể sử dụng giao diện không chặn như giao diện được sử dụng trong câu trả lời này , sau đó bạn có thể thực hiện một while(done)vòng lặp.
Keyvan

Câu trả lời:


182

bạn không thể thực hiện một vòng lặp "while (xong)" bởi vì điều đó sẽ yêu cầu chặn đầu vào, một cái gì đó node.js không muốn làm.

Thay vào đó, hãy thiết lập một cuộc gọi lại để được gọi mỗi khi có thứ gì đó được nhập:

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
Cảm ơn bạn điều này hoạt động, người nghe "kết thúc" có cho phép gọi một số hoạt động đóng và nói 'Tạm biệt' không?
Risto Novik

Tôi đã loại bỏ người nghe "kết thúc" khỏi ví dụ, tôi không biết nơi nào thực sự hữu ích để thành thật.
cướp

2
Bạn có thể đơn giản hóa đầu ra chuỗi thành d.toString (). Trim ()
MKN Web Solutions

6
Câu trả lời này ngày 2011 và nhiều thay đổi kể từ đó. Cụ thể, phần đầu tiên của câu trả lời, bạn không thể thực hiện một vòng lặp ... không giữ nữa. Có, bạn có thể có một vòng lặp while và vẫn không chặn, nhờ vào mẫu không đồng bộ đang chờ. Các câu trả lời khác phản ánh điều đó. Đối với bất cứ ai đọc điều này ngày nay - xin vui lòng tham khảo câu trả lời khác là tốt.
Wiktor Zychla

1
Để theo dõi trên @WiktorZychla, hàm process.openStdin trong khi vẫn hoạt động, đã bị từ chối vào khoảng năm 2011 và bạn sẽ không tìm thấy bất kỳ tài liệu nào liên quan đến nó.
calder.ty

111

Tôi đã sử dụng một API khác cho mục đích này ..

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

Điều này cho phép nhắc trong vòng lặp cho đến khi câu trả lời là right. Ngoài ra, nó cung cấp bàn điều khiển nhỏ xinh. Bạn có thể tìm thấy chi tiết @ http://nodejs.org/api/readline.html#readline_example_tiny_cli


11
Đây là một câu trả lời tuyệt vời. Điều có thể không rõ ràng (nhưng là một điểm cộng lớn) là đường đọc không phụ thuộc bên ngoài: Đó là một phần của node.js.
jlh

51

API Readline đã thay đổi khá nhiều kể từ 12 '. Tài liệu hiển thị một ví dụ hữu ích để nắm bắt đầu vào của người dùng từ luồng tiêu chuẩn:

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

Thêm thông tin ở đây.


5
đây chỉ là một ví dụ cơ bản Làm thế nào để bạn tương tác? trả lời câu hỏi? nhiều lựa chọn và như thế nào? Cách mở lại rl sau khi đóng, nếu không thể làm việc với rl mở để tương tác với người dùng bao gồm một số logic
Pawel Cioch

27

Tôi tin rằng điều này xứng đáng với một async-awaitcâu trả lời hiện đại , giả sử nút> = 7.x được sử dụng.

Câu trả lời vẫn sử dụng ReadLine::questionnhưng kết thúc nó sao cho while (done) {}có thể, đó là điều mà OP yêu cầu rõ ràng.

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

và sau đó là một ví dụ sử dụng

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

dẫn đến cuộc trò chuyện sau

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

Đây chính xác là câu trả lời tôi đang tìm kiếm. Tôi nghĩ rằng nó nên là một trong những hàng đầu.
William Chou

Xinh đẹp. Async await là cần thiết cho các kịch bản lớn hơn. Đây chính xác là những gì tôi cần.
Abhay Shiro

25

Vui lòng sử dụng đồng bộ hóa readline , điều này cho phép bạn làm việc với bảng điều khiển đồng bộ với các địa điểm gọi lại. Thậm chí hoạt động với mật khẩu:

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


5
Điều này đòi hỏi sự phụ thuộc thêm vì vậy tôi muốn các giải pháp khác.
Risto Novik

Không chạy trên SO "Uncaught ReferenceError: đọc không được xác định"
awwsmm

12

Câu trả lời @rob sẽ hoạt động hầu hết thời gian, nhưng nó có thể không hoạt động như bạn mong đợi với đầu vào dài.

Đó là những gì bạn nên sử dụng thay thế:

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

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

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

Giải thích tại sao giải pháp này hoạt động:

addListener('data') hoạt động như một bộ đệm, gọi lại sẽ được gọi khi nó đầy hoặc / và kết thúc đầu vào của nó.

Còn đầu vào dài thì sao? Một 'data'cuộc gọi lại sẽ không đủ, do đó bạn sẽ nhận được phần đầu vào của mình được chia thành hai hoặc nhiều phần. Điều đó thường không thuận tiện.

addListener('end')sẽ thông báo cho chúng tôi khi người đọc stdin đọc xong đầu vào của chúng tôi. Vì chúng tôi đã lưu trữ dữ liệu trước đó, bây giờ chúng tôi có thể đọc và xử lý tất cả cùng nhau.


3
Khi tôi đang sử dụng mã ở trên và chèn một số đầu vào và sau đó "nhập" phím, giao diện điều khiển tiếp tục yêu cầu tôi nhập thêm. Làm thế nào chúng ta nên chấm dứt nó?
Matan Tubul

5

Tôi khuyên bạn nên sử dụng Inquirer , vì nó cung cấp một tập hợp các giao diện người dùng dòng lệnh tương tác phổ biến.

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

Đây là một ví dụ:

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

Đầu ra:

Enter name: bob
Your name is: bob

Rất vui được trả lời anh em !! Chỉ cần đơn giản và rõ ràng.
MD.JULHAS HOSSAIN

3

Điều này là quá phức tạp. Một phiên bản dễ dàng hơn của:

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

sẽ được sử dụng

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

sau đó nó sẽ đợi khi bạn sử dụng

rl.question('string');

sau đó dễ dàng hơn để lặp lại. ví dụ:

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

Một trường hợp sử dụng phổ biến có lẽ là để ứng dụng hiển thị lời nhắc chung và xử lý nó trong câu lệnh chuyển đổi.

Bạn có thể có được một hành vi tương đương với một vòng lặp while bằng cách sử dụng hàm trợ giúp sẽ tự gọi nó trong cuộc gọi lại:

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

Bạn có thể chuyển một chuỗi trống thay vì 'app> 'nếu ứng dụng của bạn đã in thứ gì đó ra màn hình bên ngoài vòng lặp này.


2

Cách tiếp cận của tôi với điều này sẽ là sử dụng các trình tạo async .

Giả sử bạn có một loạt các câu hỏi:

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

Để sử dụng awaittừ khóa, bạn phải gói chương trình của mình vào IIFE không đồng bộ.

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

Kết quả dự kiến:

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

Nếu bạn muốn nhận được câu hỏi một câu trả lời hoàn toàn, bạn có thể đạt được điều này với một sửa đổi đơn giả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' ] ]
   */

2

Tôi đã phải viết một trò chơi "tic-tac-toe" trong Node lấy đầu vào từ dòng lệnh và viết khối mã async / await cơ bản này đã thực hiện thủ thuật này.

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

Chặn hành vi bỏ chặn đường đọc

Hãy tưởng tượng bạn có ba câu hỏi được trả lời từ bảng điều khiển, vì bây giờ bạn biết mã này sẽ không chạy vì mô-đun chuẩn readline có hành vi 'bỏ chặn' nói rằng mỗi rl.question là một luồng độc lập nên mã này sẽ không chạy.

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

Chạy đầu ra:

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)

Giải pháp đề xuất sử dụng bộ phát sự kiện để báo hiệu kết thúc chuỗi bỏ chặn và bao gồm logic vòng lặp và kết thúc chương trình vào chức năng nghe của nó.

'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

Chạy đầu ra:

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

Tôi đã thèm một đoạn script nhỏ để đọc thư mục và viết một tên giao diện điều khiển tệp mới (ví dụ: 'name.txt') và văn bản thành tệp.

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

Cách dễ nhất là sử dụng đồng bộ hóa đường đọc

Nó xử lý từng cái một và đưa ra.

npm i readline-sync

ví dụ:

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

Bạn thực sự nên bao gồm requiretuyên bố của bạn . Không có lý do để bỏ nó đi.
solidstatejake
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.