Nilai bacaan dari konsol, secara interaktif

155

Saya pikir membuat server http server sederhana dengan beberapa ekstensi konsol. Saya menemukan cuplikan untuk membaca dari data baris perintah.

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

  });

baik untuk mengajukan pertanyaan berulang kali, saya tidak bisa cukup menggunakan while(done) { }loop? Juga baik jika server menerima output pada waktu pertanyaan, itu merusak saluran.

Risto Novik
sumber
5
Saya menganggap rlmaksud Anda readline ?
jpaugh
Anda dapat menggunakan antarmuka non-pemblokiran seperti yang digunakan dalam jawaban ini , lalu Anda dapat melakukan while(done)loop.
Keyvan

Jawaban:

182

Anda tidak dapat melakukan loop "selagi (selesai)" karena itu akan membutuhkan pemblokiran pada input, sesuatu yang tidak ingin dilakukan node.js.

Alih-alih mengatur panggilan balik untuk dipanggil setiap kali sesuatu dimasukkan:

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() + "]");
  });
rampok
sumber
2
Terima kasih ini berhasil, apakah pendengar "akhir" memungkinkan untuk memanggil beberapa operasi penutupan dan mengucapkan 'Selamat tinggal'?
Risto Novik
Saya menghapus pendengar "akhir" dari contoh, saya tidak tahu di mana itu benar-benar berguna untuk jujur.
merampok
2
Anda dapat menyederhanakan output string ke d.toString (). Trim ()
MKN Web Solutions
6
Jawaban ini tanggal 2011 dan banyak yang telah berubah sejak. Secara khusus, bagian pertama dari jawaban, Anda tidak dapat melakukan loop sementara ... tidak tahan lagi. Ya, Anda dapat memiliki loop sementara dan masih tidak memblokir, berkat pola async-waiting. Jawaban lain mencerminkan hal itu. Untuk siapa pun yang membaca ini saat ini - silakan berkonsultasi dengan jawaban lain juga.
Wiktor Zychla
1
Untuk mengikuti di @WiktorZychla, function process.openStdin saat masih berfungsi, sudah tidak digunakan lagi di sekitar tahun 2011, dan Anda tidak akan menemukan dokumentasi mengenai hal itu.
calder.ty
111

Saya telah menggunakan API lain untuk tujuan ini ..

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

Ini memungkinkan untuk meminta dalam lingkaran sampai jawabannya right. Juga memberikan konsol kecil yang bagus. Anda dapat menemukan detail @ http://nodejs.org/api/readline.html#readline_example_tiny_cli

Madhan Ganesh
sumber
11
Ini jawaban yang bagus. Apa yang mungkin tidak jelas (tetapi merupakan nilai tambah besar) adalah bahwa readline bukanlah ketergantungan eksternal: Ini bagian dari node.js.
jlh
51

API Readline telah berubah sedikit sejak 12 '. Doc's menunjukkan contoh yang berguna untuk menangkap input pengguna dari aliran standar:

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

Informasi lebih lanjut di sini.

Patrick.SE
sumber
5
ini hanya contoh dasar. Bagaimana Anda berinteraksi? pertanyaan / jawaban? pilihan ganda dan sejenisnya? Cara membuka kembali rl setelah ditutup, jika tidak bisa bagaimana cara bekerja dengan rl terbuka untuk berinteraksi dengan pengguna termasuk beberapa logika
Pawel Cioch
28

Saya percaya ini layak mendapat async-awaitjawaban modern , dengan asumsi simpul> = 7.x digunakan.

Jawabannya masih menggunakan ReadLine::questiontetapi membungkusnya sehingga while (done) {}memungkinkan, yang merupakan sesuatu yang ditanyakan OP secara eksplisit.

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

dan kemudian contoh penggunaan

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

mengarah ke percakapan berikut

Are you sure? no
Are you sure? no
Are you sure? yes
finally you are sure!
Wiktor Zychla
sumber
Inilah jawaban yang saya cari. Saya pikir itu harus menjadi yang teratas.
William Chou
Cantik. Async menunggu diperlukan untuk skrip yang lebih besar. Inilah yang saya butuhkan.
Abhay Shiro
25

Silakan gunakan readline-sync , ini memungkinkan Anda bekerja dengan konsol sinkron tanpa panggilan balik. Bahkan bekerja dengan kata sandi:

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

Arango
sumber
5
Ini membutuhkan ketergantungan ekstra jadi saya lebih suka solusi lain.
Risto Novik
Tidak berjalan pada SO "Referensi Tidak Tertangkap: membaca tidak didefinisikan"
awwsmm
12

@rob jawaban akan bekerja sebagian besar waktu, tetapi mungkin tidak berfungsi seperti yang Anda harapkan dengan input panjang.

Itulah yang seharusnya Anda gunakan:

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

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

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

Penjelasan mengapa solusi ini bekerja:

addListener('data') berfungsi seperti buffer, panggilan balik akan dipanggil saat penuh atau / dan itu adalah akhir dari input.

Bagaimana dengan input panjang? Tunggal'data' panggilan balik saja tidak akan cukup, karenanya Anda akan mendapatkan input terbagi menjadi dua bagian atau lebih. Itu sering tidak nyaman.

addListener('end')akan memberi tahu kami ketika pembaca stdin selesai membaca masukan kami. Karena kami telah menyimpan data sebelumnya, kami sekarang dapat membaca dan memproses semuanya bersama-sama.

zurfyx
sumber
3
ketika saya menggunakan kode di atas dan masukkan beberapa input dan kemudian "masukkan" kunci konsol terus meminta saya untuk lebih banyak input. bagaimana kita menghentikannya?
Matan Tubul
5

Saya sarankan menggunakan Inquirer , karena menyediakan kumpulan antarmuka pengguna baris perintah interaktif umum.

const inquirer = require('inquirer');

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

const answers = await inquirer.prompt(questions);
console.log(answers);
Diogo Cardoso
sumber
5

Ini sebuah contoh:

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

Keluaran:

Enter name: bob
Your name is: bob
Miguel Mota
sumber
Jawaban saudara yang bagus !! Sederhana dan jelas.
MD.JULHAS HOSSAIN
3

Ini terlalu rumit. Versi yang lebih mudah:

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

akan digunakan

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

maka itu akan menunggu saat Anda menggunakan

rl.question('string');

maka lebih mudah diulang. sebagai contoh:

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!');
}
Ragnarok Ragdoll
sumber
2

Kasus penggunaan umum mungkin untuk aplikasi untuk menampilkan prompt generik dan menanganinya dalam pernyataan beralih.

Anda bisa mendapatkan perilaku yang setara dengan loop sementara dengan menggunakan fungsi pembantu yang akan memanggil dirinya sendiri dalam 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;
    }
});

Anda dapat melewatkan string kosong alih-alih 'app> 'jika aplikasi Anda sudah mencetak sesuatu ke layar di luar loop ini.

zoran404
sumber
2

Pendekatan saya untuk ini adalah dengan menggunakan generator async .

Dengan asumsi Anda memiliki serangkaian pertanyaan:

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

Untuk menggunakan awaitkata kunci, Anda harus membungkus program Anda menjadi IIFE async.

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

Hasil yang diharapkan:

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

Jika Anda ingin mendapatkan jawaban sekaligus pertanyaan, Anda dapat mencapainya dengan modifikasi sederhana:

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' ] ]
   */
Andrei Gătej
sumber
2

Saya harus menulis permainan "tic-tac-toe" di Node yang mengambil input dari baris perintah, dan menulis blok kode async / menunggu dasar yang melakukan trik ini.

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()
Stefan Musarra
sumber
1

Memblokir readline, membuka blokir perilaku

Bayangkan Anda memiliki tiga pertanyaan yang harus dijawab dari konsol, karena Anda sekarang tahu kode ini tidak akan berjalan karena modul standar readline memiliki perilaku 'tidak terblokir' yang mengatakan setiap rl.pertanyaan adalah utas independen sehingga kode ini tidak akan berjalan.

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

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

Solusi yang diusulkan menggunakan emitor peristiwa untuk memberi sinyal akhir dari unblocking thread, dan termasuk loop logika dan akhir program ke dalam fungsi pendengarnya.

'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

Menjalankan 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' ] ]
vlc33
sumber
0

Saya telah membuat sebuah skrip kecil untuk direktori baca dan menulis file baru nama konsol (contoh: 'name.txt') dan teks ke dalam 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();
  }
});
niksolaz
sumber
0

Cara termudah adalah menggunakan readline-sync

Ini memproses satu per satu input dan output.

npm i readline-sync

misalnya:

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)
}
Rohit Parte
sumber
Anda benar-benar harus memasukkan requirepernyataan Anda . Tidak ada alasan untuk meninggalkannya.
solidstatejake