マルチプロセスプログラミング

にほんブログ村 IT技術ブログへ

Child_Process.exec()

スクレイピングツールを高速化するため、マルチプロセスプログラミングが可能な
Child_Process モジュールの勉強中。
Child_Process は子プロセスを作成するメソッドが幾つかあり、今回は exec() について。
exec() は子プロセスを作成し、その中でシェルを起動して引数に渡されたコマンドを実行するメソッド。
Windowsの場合、cmd.exe /c XXXX という形式で実行される。実行できるコマンドはcmd.exe で実行できるものなら何でも可能。色々試してみます。

コマンドプロンプト組み込みコマンドを実行する

cmd.exe を起動してるので組み込みコマンドの dir が実行できる。

const { exec } = require('child_process');
const iconv = require('iconv-lite');
console.log("parent:" + process.pid);
const command = 'dir c:\\temp';
const child = exec(command, {encoding: 'buffer'}, (error, stdout, stderr) => {
    if (error) {
        console.error(`exec error: ${error}`);
        return;
    }
    if(stdout.length !== 0){
        console.log(iconv.decode(stdout, "Shift_JIS"));
    }
    if(stderr.length !== 0){
        console.log(iconv.decode(stderr, "Shift_JIS"));
    } 
});
console.log("child:" + child.pid);

実行結果

multi> node .\Proc_exec.js
parent:14352
child:19168

c:\temp のディレクトリ

2018/09/29 10:58 .
2018/09/29 10:58 ..
0 個のファイル 0 バイト
2 個のディレクトリ 175,356,596,224 バイトの空き領域

ポイント

  • 日本語Shift_Jisを扱うためにiconvを利用している。
  • コマンドは引数もブランク区切りでそのまま書いて、文字列で渡している。
  • 別々のプロセスなのでプロセスIDが異なっている。

バッチファイルを実行する

当然バッチファイルの実行も可能。

const { exec } = require('child_process');
const iconv = require('iconv-lite');
console.log("parent:" + process.pid);
const command = 'test.bat';
const child = exec(command, {encoding: 'buffer'}, (error, stdout, stderr) => {
    if (error) {
        console.error(`exec error: ${error}`);
        return;
    }
    if(stdout.length !== 0){
        console.log(iconv.decode(stdout, "Shift_JIS"));
    }
    if(stderr.length !== 0){
        console.log(iconv.decode(stderr, "Shift_JIS"));
    } 
});
console.log("child:" + child.pid);

test.bat

@echo off
date /t
echo exec batch

実行結果

multi> node .\Proc_exec.js
parent:14008
child:10180
2019/05/06
exec batch

PowerShellコマンドを実行する

Node.jsからPowerShellが実行できる。

const { exec } = require('child_process');
const iconv = require('iconv-lite');
console.log("parent:" + process.pid);
const command = 'powershell.exe -command "Get-Date"';
const child = exec(command, {encoding: 'buffer'}, (error, stdout, stderr) => {
    if (error) {
        console.error(`exec error: ${error}`);
        return;
    }
    if(stdout.length !== 0){
        console.log(iconv.decode(stdout, "Shift_JIS"));
    }
    if(stderr.length !== 0){
        console.log(iconv.decode(stderr, "Shift_JIS"));
    } 
});
console.log("child:" + child.pid);

実行結果

multi> node .\Proc_exec.js
parent:12172
child:13572

2019年5月6日 2:25:32

外部コマンドを実行する

kintoneのクライアントである cli-kintone.exe も実行できます。
が、何故か以下だとエラーになります。

const { exec } = require('child_process');
const iconv = require('iconv-lite');
console.log("parent:" + process.pid);
const command = 'cli-kintone.exe';
const child = exec(command, {encoding: 'buffer'}, (error, stdout, stderr) => {
    if (error) {
        console.error(`exec error: ${error}`);
        return;
    }
    if(stdout.length !== 0){
        console.log(iconv.decode(stdout, "Shift_JIS"));
    }
    if(stderr.length !== 0){
        console.log(iconv.decode(stderr, "Shift_JIS"));
    } 
});
console.log("child:" + child.pid);

実行結果

multi> node .\Proc_exec.js
parent:14812
child:13360
exec error: Error: Command failed: cli-kintone.exe

引数なしで実行すると cli-kintone.exeはヘルプの出力をするのですが、エラーになりました。
どうやらexec() は実行したコマンドの実行結果(%ERRORLEVEL%)が 0 以外の場合はエラーにするようです。cli-kintone.exe を引数なしで実行すると 1 が返却されていたのでexec() でエラー判定されていたようです。
以下の形式にすれば cli-kintone が 1 でも more の実行結果が 0 になるのでOKです。

const command = 'cli-kintone.exe | more';

cli-kintone.exe の結果をNode.jsに取り込む

外部コマンドの結果を取り込んで処理可能です。
cli-kintone.exe はアプリの取得情報をJSON形式で出力できるので、Node.js側に取り込むのが楽です。stdout の値をJSON.parse() してあげれば、Node.js 内にオブジェクトとして取り込めます。

const { exec } = require('child_process');
const iconv = require('iconv-lite');
console.log("parent:" + process.pid);
const command = 'cli-kintone.exe -d domain -a appid -t token -o json -e sjis -c dateTime,workStatus';
const child = exec(command, {encoding: 'buffer'}, (error, stdout, stderr) => {
    if (error) {
        console.error(`exec error: ${error}`);
        return;
    }
    if(stdout.length !== 0){
        var obj = JSON.parse(iconv.decode(stdout, "Shift_JIS"));
        console.log(obj.records);
    }
    if(stderr.length !== 0){
        console.log(iconv.decode(stderr, "Shift_JIS"));
    } 
});
console.log("child:" + child.pid);

実行結果

multi> node .\Proc_exec.js
parent:16884
child:12392
[ { dateTime: { type: ‘DATETIME’, value: ‘2019-05-03T10:48:00Z’ },
workStatus: { type: ‘DROP_DOWN’, value: ‘退勤’ } },
{ dateTime: { type: ‘DATETIME’, value: ‘2019-05-03T08:20:00Z’ },
workStatus: { type: ‘DROP_DOWN’, value: ‘出勤’ } },
{ dateTime: { type: ‘DATETIME’, value: ‘2019-05-03T06:15:00Z’ },
workStatus: { type: ‘DROP_DOWN’, value: ‘退勤’ } },
{ dateTime: { type: ‘DATETIME’, value: ‘2019-05-03T05:44:00Z’ },
workStatus: { type: ‘DROP_DOWN’, value: ‘出勤’ } },
{ dateTime: { type: ‘DATETIME’, value: ‘2019-05-03T03:45:00Z’ },
~~~~~~~~

Node.jsの外部スクリプトを実行する

最終的にどうなるか分かりませんが、親プロセスから子プロセスを生成し外部のNode.jsスクリプトを非同期実行する形式になるかと思います。以下の形式になるかと。

const { exec } = require('child_process');
const iconv = require('iconv-lite');
console.log("parent:" + process.pid);
const command = 'node .\\child.js';
const child = exec(command, {encoding: 'buffer'}, (error, stdout, stderr) => {
    if (error) {
        console.error(`exec error: ${error}`);
        return;
    }
    if(stdout.length !== 0){
        console.log(iconv.decode(stdout, "Shift_JIS"));
    }
    if(stderr.length !== 0){
        console.log(iconv.decode(stderr, "Shift_JIS"));
    } 
});
console.log("child:" + child.pid);

child.js

console.log("Child proc");

実行結果

multi> node .\Proc_exec.js
parent:18828
child:5164
Child proc

Node.js で外部コマンド実行できると、色々夢が広がりますね。
サーバプロセス作ってリクエスト来たら外部コマンド実行とか。何とか。
次回はちょっと横道にそれて、その辺を試してみようかと。

ブロトピ:今日のブログ更新
ブロトピ:ブログ更新通知をどうぞ!
ブロトピ:ブログ更新通知
ブロトピ:ブログ更新しました!

広告
ブログサークル
ブログにフォーカスしたコミュニティーサービス(SNS)。同じ趣味の仲間とつながろう!