エスケープシーケンスコンソール(CLI)サーバーサイドローカル環境同期・非同期

【Node.js】 コンソール(CLI)でキー入力を受け付ける

更新日:2021/10/21

Node.jsでコンソール(標準入力)からの入力を、エンターが押されるまでの文字列として取得する方法と、一文字ごとに受け取る方法をお伝えします。

 

文字列を受け付ける

コンソールでエンターキーが押されるまでに入力された文字列を取得する場合、readlineモジュールを使用します。

readlineは標準モジュールなので、追加でインストールする必要はありません。

文字列受付の基本的な流れ

readlineの使用方法は簡単です。

文字列受付の基本的な流れ


const readline = require('readline');

const readInterface = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});
readInterface.question("入力してください >",
        inputString=>{
            readInterface.close();
            console.log( `入力された文字:[${inputString }]`);
        });

まずcreateInterfaceメソッドで、文字列入力のためのインターフェースを作成します。

readlineはストリームからの入出力を処理します。
CLI上での入力を取り扱う場合は、上のコードのように標準入力と標準出力を指定します。

questionメソッドは第一引数で指定した文字列を表示した後、文字列が入力され、エンターが押されるのを待ちます。
エンターが押されると、第二引数で指定されたコールバック関数が実行されます。

コールバック関数は引数として入力された文字を受け取ります。

createInterfaceメソッドで作成したインターフェースは、closeメソッドで閉じる必要があります。
閉じないとイベント待ち状態が継続され、node.jsのプロセスが終了しません。

Promise版

v16以前はreadlineのPromise版が用意されていないので、独自に実装します。

v16以前 Promiseでの文字列受付の基本的な流れ


const readline = require('readline');

const inputString = prompt =>{
    const readInterface = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
    });
    return new Promise( resolive =>readInterface.question(prompt,
        inputString=>{
            readInterface.close();
            resolive( inputString);
        }));
};

(async ()=>{
const string = await inputString("文字列を入力してください >");

console.log( string );
})();

v17からPromise版のreadlineが追加されました。

v17以降 Promiseでの文字列受付の基本的な流れ


const readline = require('readline/promises');

(async ()=>{
    const readInterface = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
    });
    const string = await readInterface.question("文字列を入力してください >");

    console.log( string );
    readInterface.close();
})();

入力を中断する(制限時間を設ける)

文字入力をプログラムコードで中断する場合、AbortControllerを使用します。
次のコードは、一定時間経過したらタイムアウトとして入力待ちを中断しています。


const readline = require('readline');

const inputString = (prompt,timeout = undefined) =>new Promise( (resolve,reject) =>{
        const readInterface = readline.createInterface({
            input: process.stdin,
            output: process.stdout,
        });
        const ac = new AbortController();

        const timer =  timeout ?  setTimeout( ()=>{
            ac.abort();
            readInterface.close();
            reject(new Error("タイムアウトしました"));
            
        },timeout) : null;

        readInterface.question(prompt,{ signal:ac.signal },
            inputString=>{
                readInterface.close();
                clearTimeout(timer);
                resolve( inputString);
            })
    });
(async ()=>{
const string = await inputString("文字列を入力してください >",3000).catch(e=>e.message);

console.log( string );
})();

questionメソッドの第二引数に、signalプロパティを持つオブジェクトを指定します。
このプロパティの値には、AbortControllerのインスタンスのsignalプロパティをセットします。

そしてタイマー満了時に、AbortControllerのインスタンスのabortメソッドを実行すると、questionでの読み取りが終了します。
このとき、readlineのインターフェースは開いたままなので、忘れずに閉じます。

 

一文字だけ読み取る

標準入力のdataイベントを捕捉すると、キーボードで文字が入力されるたびに、何らかの処理を行えます。

キー入力を取得する


process.stdin.setEncoding( "utf8" );
process.stdin.setRawMode( true );
process.stdin.resume( );

const CtrlC = "\u0003";

process.stdin.on( "data", key=>{
    if( key === CtrlC ) process.exit();
    process.stdout.write( key );
});

デフォルトではエンコードが指定されていないため、イベントで受け取る値はBufferオブジェクトです。
setEncodingで"utf8"をセットすると、文字列で受け取ることができます。

また、一文字ごとに文字を受け取るには、setRawModeで標準入力をrawモードに変更する必要があります。

resumeは停止しているストリーム読み込みを、再開します。

読み取り中はCtrl+Cでの中断が無効になります。
そのため、入力されたデータを確認しています。

上のコードを、特定のキー(yまたはn)が押されるまで待つように変更してみます。

特定のキーが押されるまで待つ


process.stdin.setEncoding( 'utf8' );
const CtrlC = "\u0003";

const inputKey = (prompt)=>new Promise( resolve => {

    const stdin = process.stdin;
    const isRow = stdin.isRaw;
    const callBack = key =>{
        process.stdout.write( key );
        process.stdout.write( "\n" );
        stdin.off( "data" , callBack );
        stdin.pause();
        stdin.setRawMode( isRow );
        resolve( key );
    };
    stdin.setRawMode( true );
    stdin.resume( );
    process.stdout.write( prompt );
    stdin.on( "data", callBack );
});

(async ()=>{

    let key;
    do{
        key = (await inputKey("よろしいですか?(y/n) >" )).toUpperCase();
        if( key === CtrlC ) process.exit(-1);
    }while( ["Y","N"].indexOf(key) < 0 );

    console.log( `${key}が押されました`);
})();

更新日:2021/10/21

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。