Node.js 簡単な応答バッチ処理

# はじめに
Node.jsでyまたはnの応答に従い、バッチ処理を実行するJavaSciptのサンプルを作成しました。
この辺、Promiseとasync/awaitなどを利用して対話的に同期処理を実行すると分けがわからなくところです。
初学者向けと自らの備忘録で掲載します。

# 前提
* OS : Windows7以上
* [Git For Windows 構築ずみ](https://qiita.com/toshi-click/items/dcf3dd48fdc74c91b409)でbashターミナルを使用
* VSCodeでコード編集
* node.js環境構築済み

# バッチ処理リスト

* **batchlist.json**

```Json
[
    {"msg" : "★ ★　リストを確認します。(ls) 実行 = y, 中止 = n　★ ★",
     "con" : "y",
     "can" : "n",
     "cmd" : "ls"
    },
    {"msg" : "★ ★　パスを確認します。(pwd) 実行 = y, 中止 = n　★ ★",
     "con" : "y",
     "can" : "n",
     "cmd" : "pwd"
    }
]
```

## JSON項目説明

|項目|説明|
|:--|:--|
|msg|応答文、実行と中止を定義|
|con|実行(continue)の文字列を定義|
|can|中止(cancel)の文字列を定義|
|cmd|実行するコマンド(command)を定義|


# バッチ処理スクリプト

* **Dobatch.js**

```JavaScript

const exec = require('child_process').execFileSync;
const fs = require('fs');
const json = JSON.parse(fs.readFileSync('./batchlist.json', 'utf8'));
// コンソール標準入出力
const rl = require("readline").createInterface(process.stdin, process.stdout);
const gets=()=>new Promise(res => rl.once("line",res));

/**
 * コマンドラッパー(同期処理)
 * @param {String } cmd - コマンド
 */
 function sh(cmd) {
    try { 
        console.log(exec(cmd, { shell: true }).toString());
    } catch(error) { 
        console.log(error.stderr.toString()); }
}

// 処理実行
(async function() {
    for (var i = 0 ; json.length > i ; i++ ) {
        console.log(json[i].msg); 
        str = await gets();
        if ( str == json[i].con ) {
            console.log(json[i].cmd);
            sh(json[i].cmd);
        } else if ( str == json[i].can ) {
            process.exit();
        } else {
            console.log(str + "は入力不正です。")
            i--;
        }
    }
    process.exit();
})();

```

## 説明
* コマンド実行はchild_processのexecFileSyncを利用して同期処理
* JSONの読み出しもfsのreadFileSyncを利用して同期処理
* 残るは、コンソールからの入出力応答をPromoiseとasync/awaitで同期処理  
  gets関数で定義

# 実行

```Shell
Windows PowerShell
Copyright (C) 2009 Microsoft Corporation. All rights reserved.

PS C:\Users\～>bash --version ...①
GNU bash, version 4.4.12(2)-release (x86_64-pc-msys)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
PS C:\Users\～>bash ...②
USER MINGW64 ~/
$ node Dobatch.js ...③
★ ★　リストを確認します。(ls) 実行 = y, 中止 = n　★ ★
y
ls
batchlist.json
Dobatch.js

★ ★　パスを確認します。(pwd) 実行 = y, 中止 = n　★ ★
y
pwd
/c/Users/～/


USER MINGW64 ~/
$
```

## 説明
① bashのバージョン確認
② PowerShellからbashへ切替え
③ Dobatch.jsの実行

※ 実行で表示されるパスは出力を編集して表示しています。

# まとめ
Node.jsのコンソールからの標準入力がめんどくさい、VBのMsgBox的なコーディングをしようとして悪戦苦闘した記憶がありました。ファイルの読み込みとコマンド実行を同期処理にすれば、以外と簡単にできると思います。たぶレスポンスはいまいちだと思います。
色々なシステム導入ツールがあると思いますが、JSONファイルのバッチ定義で簡易システム導入ツールとしてご利用してみてください。ただし、参考までに、例によって一切のご利用はあくまでも自己責任でお願いします。

# 仮想ターミナルでの実行
2019.12.19 更新
execFileSyncでコマンドを実行するたび、子プロセスを作成するので、指定した環境で連続したコマンドの実行に向かないので、spawn('bash')で子プロセスのターミナルを作成して、その子プロセスに、stdin.writeでコマンドを書き込んで実行する方法を別に作成しましたのでアップします。

*  "con" : "auto"を指定すると自動実行の機能を追加。
*  "skp" : "s" 処理のスキップ機能を追加。
*  msgの出力をカラーにしたいので、colorsを追加しました。
*  バッチリストを引数にしました。

```sh
npm install --save colors
```
## 実行

```sh
node Dobatch.js batchlist.json
```
## Dobatch.js 改

```JavaScript
require('colors');

// バッチリスト
if ( process.argv[2] == null ) { 
    console.log("バッチリストを指定してください。".red.bold);
    process.exit();
};

const fs = require('fs');

// バッチリスト存在チェック
try { fs.statSync(process.argv[2]);
    console.log("input list : " + process.argv[2] + "\n");
} catch(err) {
    if( err.code === 'ENOENT') {
        console.log(process.argv[2] + " が存在しません。".red.bold); 
        process.exit();        
    }
}

const json = JSON.parse(fs.readFileSync(process.argv[2]),'UTF-8');
const terminal = require('child_process').spawn('bash');

// コンソール標準入出力
const rl = require("readline").createInterface(process.stdin, process.stdout);
const gets=()=>new Promise(res => rl.once("line",res));

/**
 * コマンドラッパー
 * @param {String} cmd - コマンド
 */
function sh(cmd) {
    return new Promise((resolve) => {
        try {
            terminal.stdin.write(cmd + '\n');
            terminal.stdout.once('data', function(data) {
                console.log(data.toString());
                resolve(data.toString());
            })
        } catch(error) {
            console.log(error.stderr);
        }
        return;
    });
}

// 処理実行
(async function() {
    for (var i = 0 ; json.length > i ; i++ ) {
        console.log(json[i].msg.yellow);

        // auto判定
        if ( json[i].con == "auto" ) {
            if ( json[i].cmd == "" ) {
                continue;
            } else {
                console.log(json[i].cmd.green);
                await sh(json[i].cmd);
            }
        } else {
            str = await gets();
            if ( str == json[i].con ) {
                if ( json[i].cmd == "" ) {
                    continue;
                } else {
                    console.log(json[i].cmd.green);
                    await sh(json[i].cmd);
                }
            } else if ( str == json[i].can ) {
                process.exit();
            } else if ( str == json[i].skp ) {
                continue;
            } else {
                console.log(str + "は入力不正です。".red.bold)
                i--;
            }
        }
    }
    process.exit();
})();
```