child_process

child_process

const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`輸出:${data}`);
});

ls.stderr.on('data', (data) => {
  console.log(`錯誤:${data}`);
});

ls.on('close', (code) => {
  console.log(`子進程退出碼:${code}`);
});

默認狀況下,Node.js 的父進程與衍生的子進程之間會創建 stdin、stdout 和 stderr 的管道。

child_process.exec

衍生一個 shell 並在 shell 中執行 command,並緩衝產生的輸出。 command 會被 shell 直接執行,因此 command 中的特殊字符(因shell而異)須要相應的處理:
exec('"/path/to/test file/test.sh" arg1 arg2');
// 使用雙引號,則路徑中的空格不會被當成多個參數。

exec('echo "The \\$HOME variable is $HOME"');
// 第一個 $HOME 會被轉義,第二個則不會。
若是指定了 callback,則調用時會傳入參數 (error, stdout, stderr)。 當成功時,error 會是 null。 當出錯時,error 會是 Error 實例。 error.code 屬性是子進程的退出碼,error.signal 是終止進程的信號。 任何非 0 的退出碼都會被視爲出錯。

傳給 callback 的 stdout 和 stderr 包含子進程的輸出。 默認狀況下,Node.js 會將輸出解碼成 UTF-8 字符串。 encoding 用於指定解碼 stdout 和 stderr 的字符編碼。 若是 encoding 是 'buffer' 或無效的字符編碼,則傳入 callback 的會是 Buffer。

const { exec } = require('child_process');
exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => {
  if (error) {
    console.error(`執行出錯: ${error}`);
    return;
  }
  console.log(`stdout: ${stdout}`);
  console.log(`stderr: ${stderr}`);
});
若是 timeout 大於 0,則當子進程運行超過 timeout 毫秒時,父進程就會發送帶 killSignal 屬性(默認爲 'SIGTERM')的信號。

不像 POSIX 中的 exec(3), child_process.exec() 不會替換現有的進程,且使用 shell 來執行命令。

若是調用此方法的 util.promisify() 版本,則返回的 Promise 會返回具備 stdout 屬性和 stderr 屬性的對象。

const util = require('util');
const exec = util.promisify(require('child_process').exec);

async function lsExample() {
  const { stdout, stderr } = await exec('ls');
  console.log('stdout:', stdout);
  console.log('stderr:', stderr);
}
lsExample();

child_process.spawn

運行 ls -lh /usr,並捕獲 stdout、stderr、以及退出碼:

const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.log(`stderr: ${data}`);
});

ls.on('close', (code) => {
  console.log(`子進程退出碼:${code}`);
});

運行 ps ax | grep ssh:
const { spawn } = require('child_process');
const ps = spawn('ps', ['ax']);
const grep = spawn('grep', ['ssh']);

ps.stdout.on('data', (data) => {
  grep.stdin.write(data);
});

ps.stderr.on('data', (data) => {
  console.log(`ps stderr: ${data}`);
});

ps.on('close', (code) => {
  if (code !== 0) {
    console.log(`ps 進程退出碼:${code}`);
  }
  grep.stdin.end();
});

grep.stdout.on('data', (data) => {
  console.log(data.toString());
});

grep.stderr.on('data', (data) => {
  console.log(`grep stderr: ${data}`);
});

grep.on('close', (code) => {
  if (code !== 0) {
    console.log(`grep 進程退出碼:${code}`);
  }
});
const { spawn } = require('child_process');

const subprocess = spawn(process.argv[0], ['child_program.js'], {
  detached: true,
  stdio: 'ignore'
});

subprocess.unref();

fork

衍生一個新的 Node.js 進程,並經過創建一個 IPC 通信通道來調用一個指定的模塊,該通道容許父進程與子進程之間相互發送信息
fork方法返回一個隱式建立的表明子進程的ChildProcess對象
子進程的輸入/輸出操做執行完畢後,子進程不會自動退出,必須使用process.exit()方法顯式退出

args 運行該文件模塊文件時許喲啊使用的參數
options 選項對象
cwd 指定子進程當前的工做目錄
env 屬性值爲一個對象,用於以"鍵名/鍵值"的形式爲子進程指定環境變量
encoding 屬性值爲一個字符串,用於指定輸出及標準錯誤輸出數據的編碼格式,默認值爲'utf8'
silent 屬性值爲布爾值,當屬性值爲false時,子進程和父進程對象共享標準(輸入/輸出),true時不共享

child.send(message,[sendHandle]);//在父進程中向子進程發送消息
process.send(message,[sendHandle]);//在子進程中向主進程發送消息

let {
    fork
} = require('child_process');
let path = require('path');
let child = fork(path.join(__dirname, 'fork.js'));
child.on('message', function (m) {
    console.log('父進程接收到消息:', m);
    process.exit();
});
child.send({
    name: 'zfpx'
});
child.on('error', function (err) {
    console.error(arguments);
});

//fork.js
process.on('message', function (m, setHandle) {
    console.log('子進程收到消息:', m);
    process.send({
        age: 9
    });
})
在默認狀況下子進程對象與父進程對象共享標準輸入和標準輸出。若是要讓子進程對象用獨立的標準輸入輸出,能夠將silent屬性值設置爲 true forkslient.js

let {
    fork
} = require('child_process');
let path = require('path');

let p1 = fork('node', [path.join(__dirname, 'fork1.js')], {
    silent: true
});
let p2 = fork('node',path.join(__dirname, 'fork2.js'));
p1.stdout.on('data', function (data) {
    console.log('子進程1標準輸出:' + data);
    p2.send(data.toString());
});
p1.on('exit', function (code, signal) {
    console.log('子進程退出,退出代碼爲:' + code);
});
p1.on('error', function (err) {
    console.log('子進程開啓失敗:' + err);
    process.exit();
});

//fork1.js
process.argv.forEach(function (item) {
    process.stdout.write(item + '\r\n');
});

//fork2.js
let fs = require('fs');
let out = fs.createWriteStream(path.join(__dirname, 'msg.txt'));
process.on('message', function (data) {
    out.write(data);
});

exec

exec方法能夠開啓一個用於運行某個命令的子進程並緩存子進程的輸出結果
spawn是一個異步方法,exec是一個同步方法
衍生一個 shell 並在 shell 上運行命令
child_process.exec(command,[options],[callback]);
command 須要執行的命令
options 選項對象
cwd 子進程的當前工做目錄
env 指定子進程的環境變量
encoding 指定輸出的編碼
timeout 子進程的超時時間
maxbuffer 指定緩存標準輸出和錯誤輸出的緩存區最大長度
killSignal 指定關閉子進程的信號,默認值爲 "SIGTERM"
callback 指定子進程終止時調用的回調函數
function(err,stdout,stderr){}
err 錯誤對象
stdout 標準輸出
stderr 錯誤輸出

let {
    exec
} = require('child_process');
let path = require('path');
let p1 = exec('node test1.js a b c', {
    cwd: path.join(__dirname, 'test3')
}, function (err, stdout, stderr) {
    if (err) {
        console.log('子進程開啓失敗:' + err);
        process.exit();
    } else {
        console.log('子進程標準輸出\r\n' + stdout.toString());
        p2.stdin.write(stdout.toString());
    }
});
let p2 = exec('node test2.js', {
    cwd: path.join(__dirname, 'test3')
}, function (err, stdout, stderr) {
    process.exit();
});

execFile

可使用execFile開啓一個專門用於運行某個可執行文件的子進程
相似 child_process.exec(),但直接衍生命令,且無需先衍生一個 shell

child_process.execFile(file,[args],[optioins],[callback]);

file 指定須要運行的可執行文件路徑及文件名
args 運行該文件所須要的參數
options 開啓子進程的選項
callback 指定子進程終止時調用的回調函數

let {
    execFile
} = require('child_process');
let path = require('path');

let p1 = execFile('node', ['./test1.js'], {
    cwd: path.join(__dirname, 'test4')
}, function (err, stdout, stderr) {
    if (err) {
        console.log('子進程1開啓失敗:' + err);
        process.exit();
    } else {
        console.log('子進程標準輸出:' + stdout.toString());
        p2.stdin.write(stdout.toString());
    }
});
let p2 = execFile('node', ['./test2.js'], {
    cwd: path.join(__dirname, 'test4')
}, function (err, stdout, stderr) {
    if (err) {
        console.log('子進程2開啓失敗:' + err);
        process.exit();
    } else {
        console.log('子進程標準輸出:' + stdout.toString());
    }
});

cluster

爲了利用多核CPU的優點,Node.js提供了一個cluster模塊容許在多個子進程中運行不一樣的Node.js應用程序。

fork方法建立work對象 

可使用fork方法開啓多個子進程,在每一個子進程中建立一個Node.js應用程序的實例,而且在該應用程序中運行一個模塊文件。
fork方法返回一個隱式建立的work對象
在cluster模塊中,分別提供了一個isMaster屬性與一個isWork屬性,都是布爾值
cluster.fork([env]);

獲取全部的worker

for(let index in cluster.workers){
    console.log(cluster.workers[index]);
}

獲取當前的worker和id

if(cluster.isMaster){
  cluster.fork()
}else if(cluster.isWorker){
  console.log('I am worker #'+cluster.worker.id);
}

服務器

let cluster = require('cluster');
let http = require('http');
if (cluster.isMaster) {
    cluster.fork();
    console.log('這段代碼運行在主進程裏');
} else {
    http.createServer(function (req, res) {
        if (req.url != '/favicon.ico') {
            res.end('hello');
            console.log('這段代碼運行在子進程裏');
        }
    }).listen(8080);
}
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`主進程 ${process.pid} 正在運行`);

  // 衍生工做進程。
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`工做進程 ${worker.process.pid} 已退出`);
  });
} else {
  // 工做進程能夠共享任何 TCP 鏈接。
  // 在本例子中,共享的是 HTTP 服務器。
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('你好世界\n');
  }).listen(8000);

  console.log(`工做進程 ${process.pid} 已啓動`);
}
本站公眾號
   歡迎關注本站公眾號,獲取更多信息