child_process用法
child_process的api:
- 異步api:
- exec(cmd, options, callback)
- execFile(cmd, args, options, callback)
- fork (模塊路徑, args, options) // 不一樣的地方在于可以通信
- spawn(cmd, args, options)
- exec用法:
執(zhí)行shell腳本, 使用管道符也是可以的
exec也是可以執(zhí)行文件的,只不過不能傳參數(shù)
適合開銷比較小的任務
const cp = require('child_process') cp.exec('ls -al|grep node_modules', { timeout: 0, // 超時時間 cwd: process.cwd(), // 可以改變當前的執(zhí)行路徑 }, function (err, stdout, stderr) { // 執(zhí)行結(jié)果 })- execFile用法:
可以執(zhí)行文件,也可以執(zhí)行語句,可傳參
適合開銷比較小的任務
// 執(zhí)行文件,參數(shù) cp.execFile('ls', ['-al'], function (err, stdout, std,err) => { // 執(zhí)行結(jié)果 }) // 讓execFile執(zhí)行l(wèi)s -al|grep node_modules這種語句 test.shell: ls -al|grep node_modules echo $1 // 打印參數(shù) echo $2 index.js: cp.execFile(path.resolve(__dirname, 'test.shell'), ['-al', 'bl'], function(err, stdout, stderr) { })- fork用法
用node執(zhí)行, 耗時操作且用node實現(xiàn),如下載文件
// cp.fork(模塊路徑) // 和require一樣把文件執(zhí)行起來 const child = cp.fork(path.resolve(__dirname, 'child_process')) console.log(process.pid) // 主進程向子進程通信 child.send('hello child_process', () => { // child.disconnent() // 如果不斷開,兩邊會出現(xiàn)等待的情況 }) // 子進程向主進程通信 child.on('message', msg => { }) // child_process.js: console.log('aaa', process.pid) process.on('message', msg => { console.log(msg) // 很容易出現(xiàn)死循環(huán) }) process.send('send msg to parent') // 進程不一樣,完全獨立,本質(zhì)也是調(diào)用spawn- spawn 用法
spawn: 流式的,沒有回調(diào),適合耗時任務(比如:npm install), 需要不斷打印日志(不斷給用戶輸出日志)
cp.spawn(file, args, options) // 不支持回調(diào), exec,execFile底層都是spwan
const child = cp.spawn(path.resolve(__dirname, 'test.shell'), ['-al', '-bl'], {
cwd: path.resolve('..'),
}) // 返回的是子進程
console.log(child.pid, process.pid)
// 監(jiān)聽成功
child.stdout.on('data', function(chunk) {
console.log(chunk.toString())
})
// 監(jiān)聽失敗
child.stderr.on('data', function(chunk) {
console.log(chunk.toString())
})
const code = `require('${rootFile}').call(null, ${JSON.stringify(args)})`
// cp.spawn('cmd', ['/c', 'node', '-e', code]) // win下是這種結(jié)構(gòu)
const child = spawn('node', ['-e', code], {
cwd: process.cwd(), // 當前執(zhí)行未知的cwd
stdio: 'inherit', // 默認是pipe,pipe必須通過on來接收信息,inherit不需要,實時反饋
})
child.on('error', e => {
log.error(e.message)
process.exit(1)
})
child.on('exit', e => {
log.verbose('命令執(zhí)行成功', e)
process.exit(e)
})
- 同步api:
- execSync
- execFileSync
- spawnSync
const ret = cp.execSync('ls -al|grep node_modules') // 用的比較多,對腳本安全性沒有校驗
// 可以直接拿到結(jié)果
console.log(ret.toString())
const ret2 = cp.execFileSync('ls', ['-al'])
console.log(ret2.toString)
const ret3 = cp.spawnSync('ls', ['-al'])
console.log(ret3.stdout.toString()) // 返回的是一個對象
child_process原理
1. exec 和 execFile有什么區(qū)別?
- 二者只有傳參不同
- 底層調(diào)用的還是spawn
// exec源碼,處理一下參數(shù),調(diào)用的execFile
function exec(command, options, callback) {
const opts = normalizeExecArgs(command, options, callback);
return module.exports.execFile(opts.file,
opts.options,
opts.callback);
}
function execFile(file /* , args, options, callback */) {
// ...
// 底層調(diào)用的還是spawn
const child = spawn(file, args, {
cwd: options.cwd,
env: options.env,
gid: options.gid,
shell: options.shell,
signal: options.signal,
uid: options.uid,
windowsHide: !!options.windowsHide,
windowsVerbatimArguments: !!options.windowsVerbatimArguments
});
}
- exec: 原理是調(diào)用/bin/sh -c 執(zhí)行我們傳入的shell腳本,底層調(diào)用execFile
- execFile:原理是直接執(zhí)行我們傳入的file和args,底層調(diào)用spawn創(chuàng)建和執(zhí)行子進程,并建立回調(diào),一次性將所有的stdout和stderr結(jié)果返回
- spawn:原理是調(diào)用internal/child_process,實例化略ChildProcess子進程對象,再調(diào)用child.spawn創(chuàng)建 子進程并執(zhí)行命令,底層是調(diào)用了child.)handle.spawn執(zhí)行process_wrap中的spwan方法,執(zhí)行過程是異步的,執(zhí)行完畢后再通過PIPE進行單向數(shù)據(jù)通信,通信結(jié)束后子進程發(fā)起onexit回調(diào),同時Socket會執(zhí)行close回調(diào)。
- fork:原理是通過spawn創(chuàng)建子進程和執(zhí)行命令,采用node執(zhí)行命令,通過setupchannel創(chuàng)建IPC用于子進程和父進程之間的雙向通信。
2. data/error/exit/close回調(diào)的區(qū)別
data:用于主進程讀取數(shù)據(jù)過程中通過onStreamRead發(fā)起的回調(diào)
error: 命令執(zhí)行失敗后發(fā)起的回調(diào)
exit: 子進程關(guān)閉完成后發(fā)起的回調(diào)
close:子進程所有Socket通信端口全部關(guān)閉后發(fā)起的回調(diào)
stdout close/stderr close:特定的PIPE讀取完成后調(diào)用onReadableStreamEnd關(guān)閉Socket時發(fā)起的回調(diào)。