本文环境:
- windows
- node v12.16.1
本文使用的模块:
1 | const cp = require("child_process"); |
child_process 模块提供了 4 种方法创建子进程:
- child_process.spawn
- child_process.execFile
- child_process.exec
- child_process.fork
除此之外, 还用 3 种同步方法:
- child_process.spawnSync
- child_process.execFileSync
- child_process.execSync
child_process.spawn
使用 spawn 执行 cmd 命令:
在 window 下需要使用 iconv.decode 将其输出解码为 gbk, 否则会乱码.
1 | const child = cp.spawn("ping", ["www.baidu.com"]); |
使用 spawn 执行 cmd/bat 脚本文件
子进程 echo.bat 代码:
1 | echo "bat 启动" |
主进程代码:
1 | // spawn |
注意: spawn 不支持诸如 ./ 的路径.
1 | const child = cp.spawn("./echo.bat"); |
使用 stderr 捕获错误
子进程 error.bat 代码:
1 | printt www.baidu.com |
主进程代码:
1 | // spawn |
正常退出时退出码为 0, 出错退出时退出码为 1.
使用 spawn 运行 js 文件
子进程 child.js 的代码:
1 | console.log("子进程 启动"); |
主进程代码:
1 | const child = cp.spawn("node", ["./child.js"]); |
让 spawn 支持 ipc 通信
子进程代码:
1 | console.log("子进程启动"); |
主进程代码:
1 | const child = cp.spawn("node", ["./child.js"], { |
输出结果,代码运行完成后不会自动退出.
options.stdio | Node.js API 文档
Electron 中使用 fork()函数的坑
child_process.exec
child_process.exec 在内部调用 child_process.execFile
exec 执行 cmd 命令:
1 | const child = cp.exec("ping www.baidu.com", { encoding: "buffer" }); |
注意:
exec 或默认设置 encoding 为 utf-8, 这可能会导致控制台乱码.
为了避免输出乱码, 需要设置 encoding: 'buffer', 同时用 iconv.decode 解码为 gbk.
Node.js 调用 cmd 输出中文乱码_JavaScript_liuyaqi1993 的博客-CSDN 博客
exec 不需要把执行命令的参数拆开来写, 直接用空格分开即可.
1 | cp.spawn("ping", ["www.baidu.com"]); |
输出错误信息:
1 | const child = cp.exec("pingt www.baidu.com", { encoding: "buffer" }); |
exec 方法的第三个参数接收一个回调函数, 这个函数会在子进程运行结束后被调用.
1 | cp.exec( |
使用 exec 运行 js 文件:
1 | const child = cp.exec("node child.js"); |
exec execFile fork 对于 stdout 和 stderr 接收的数据有大小限制(spawn 没有这个限制) ,可通过 maxBuffer 控制, 默认值为 1024 x 1024.
超过 maxBuffer 限制会导致如下错误:
1 | RangeError [ERR_CHILD_PROCESS_STDIO_MAXBUFFER]: stdout maxBuffer length exceeded |
该错误须通过调用 exec 时传入的回调函数获取, child.stderr 无法抓取到该错误.
子进程 child.js 内容:
1 | console.log("abc"); |
主进程代码:
1 | const child = cp.exec( |
child_process.execFile
child_process.execFile 在内部调用 child_process.spawn 方法
child_process.execFile 类似 child_process.exec, 区别如下:
1 execFile 不会衍生 shell 进程,效率稍微比 exec 高
2 execFile 在 windows 下不支持 I/O 重定向和文件通配等行为,例如下列命令 execFile 就无法执行
1 | cp.execFile("dir", { encoding: "buffer" }, (err, stdout) => { |
在 windows 环境下没必要使用 execFile
node.js - exec vs execFile nodeJs - Stack Overflow
child_process.fork
child_process.exec 方法专门用于生成 node 进程, 在内部调用 child_process.exec 方法.
子进程 child.js 代码:
1 | console.log("子进程启动"); |
主进程 index.js 代码:
1 | const child = cp.fork("./child.js"); |
inspect 导致端口被占用的解决办法
使用 fork 时, 子进程会默认使用主进程的 execArgv 参数.当子进程启动时, 主进程的 inspect 已经占用了 9229 端口,使子进程无法启动.
例如使用如下命令启动 inde.js:
1 | node --inspect index.js |
主进程代码:
1 | const child = cp.fork("./child.js"); |
端口被占用,无法启动
解决办法 1,更换端口:
1 | const child = cp.fork("./child.js", [], { |
inspect 的非默认端口需要在 chrome://inspect 页面的 configure 选项中手动添加
或是覆盖掉原来的 execArgv:
1 | const child = cp.fork("./child.js", [], { |
debugging - How to debug Node.JS child forked process? - Stack Overflow
另外测试时发现 --inspect 启动相对较慢, 有些 debugger 似乎被忽略掉了. 最好用 --inspect-brk 代替.
1 | console.log("子进程启动"); |
用 --inspect-brk 代替, 保证遇到 debugger 能停下来等待调试.
1 | const child = cp.fork("./child.js", [], { |
用 ndb 调试多进程比用 inspect 调试好很多. 如果使用 ndb 调试, 务必要保证 execArgv 里没有 --inspect, 否则遇到 debugger 关键字时不会自动停下来.