シェルコマンドを実行する方法(child_process)

Node.jsでシェルコマンドを実行させる方法はいくつか存在します。ここでは、「exec」「execSync」「spawn」について動作の違いを確認します。

exec
(非ストリーム形式, 非同期実行)

exec の動作確認をします。exec.js というファイルに以下処理を記述します。

const { exec } = require('child_process')

exec('ls -l sample.txt', (err, stdout, stderr) => {
    if (err) {
      console.log(`stderr: ${stderr}`)
      return
    }
    console.log(`stdout: ${stdout}`)
  }
)
console.log('わくわくBank')

コールバックでコマンドの出力結果を取得できます。

$ node exec.js 
わくわくBank
stdout: -rwxr-xr-x  1 xxx  xxx  21166 Nov 22 01:02 sample.txt

非同期なので わくわくBank の出力が先に行われています。

ls の部分を存在しないコマンド( aaa )にしてエラーを発生させてみます。

$ node exec.js 
わくわくBank
stderr: /bin/sh: aaa: command not found

execSync
(非ストリーム形式, 同期実行)

同期実行させたい場合、execSync を利用します。

execSync の動作確認をします。execSync.js というファイルに以下処理を記述します。

const { execSync } = require('child_process')

const stdout = execSync('ls -l sample.txt')
console.log(`stdout: ${stdout.toString()}`)
console.log('わくわくBank')
$ node execSync.js 
stdout: -rwxr-xr-x  1 xxx  xxx  21166 Nov 22 01:02 sample.txt

わくわくBank

spawn
(ストリーム形式, 非同期実行)

大きいデータを扱う場合、ストリーム形式である spawn のほうが適しています。
(一度に全てのデータをメモリに読み込むとメモリ不足になるため)

spawn の動作確認をします。spawn.js というファイルに以下処理を記述します。

const { spawn } = require('child_process')

const childProcess = spawn('cat', ['sample.txt'])

console.log('process id:' + process.pid)
console.log('child process id:' + childProcess.pid)

childProcess.stdout.on('data', (chunk) => {
  console.log(new Date())
  console.log(chunk.length)
  // console.log(chunk.toString())
})
$ node spawn.js 
process id:44066
child process id:44093
2019-11-21T16:32:48.073Z
8192
2019-11-21T16:32:48.076Z
8192
2019-11-21T16:32:48.076Z
4782

spawn の場合、上記のように少しずつデータを取得しています。

比較として execSync の動作確認を行います。

const { execSync } = require('child_process')

const stdout = execSync('cat sample.txt')
console.log(`stdout: ${stdout.length}`)

execSync の場合、以下のように一括でデータを取得しています。

$ node execSync.js 
stdout: 21166

参考