使用inquirer和child_process开发的CLI按CTRL+C无法退出全部进程怎么办
解决方案
问题根源
问题是两个原因共同导致的:
execSync运行的npm run start会生成嵌套子进程,Node.js默认不会将CTRL+C触发的SIGINT信号转发给这些子进程,主进程退出后子进程变成孤儿进程在后台持续运行- inquirer在进程异常退出时没有正常还原终端TTY的交互状态,导致终端无响应
修改后的完整代码
const inquirer = require('inquirer') const { spawn } = require('child_process') const { readdirSync } = require('fs') const targetFolder = './src/pages' const directories = readdirSync(targetFolder) let childProcess = null // 监听CTRL+C信号,主动清理子进程后退出 process.on('SIGINT', () => { if (childProcess) { // 杀死整个子进程树 process.kill(-childProcess.pid) } process.exit(0) }) const questions = [ { type: 'list', name: 'page', message: 'Choose page to compile', choices: directories, }, ] inquirer.prompt(questions).then(({ page }) => { // 用spawn替代execSync,方便管理子进程实例 childProcess = spawn('npm', ['run', 'start', '--', '--env', `page=${page}`], { stdio: 'inherit', // 类Unix系统下创建新进程组,方便一次性杀死所有嵌套的子进程 detached: process.platform !== 'win32' }) // 子进程退出后主进程同步退出 childProcess.on('exit', (code) => { process.exit(code) }) })
关键改动说明
- 把同步的
execSync换成异步的spawn,可以拿到子进程实例做后续管理 - 添加SIGINT信号监听,收到CTRL+C信号时主动杀死整个子进程树,再退出主进程
- 类Unix系统下开启
detached配置,创建独立进程组,方便一次性杀死所有嵌套的子进程 - 子进程退出后主进程同步退出,避免遗留进程
跨平台兼容说明
如果需要兼容Windows系统,建议安装cross-spawn包替换原生的child_process.spawn,可以自动处理Windows下npm执行路径和进程杀死的兼容问题,安装命令为npm i cross-spawn,之后把代码里的const { spawn } = require('child_process')替换为const spawn = require('cross-spawn')即可。
内容的提问来源于stack exchange,提问作者Hanzo Hattori
相关产品推荐
相关产品推荐

