我有一个脚本,它创建一个临时文件,然后做一些其他事情,当脚本退出时,它会删除临时文件。
control
+ C
时,临时文件将被删除。但是当我在另一个 NodeJS 脚本中运行此脚本并使用
process.kill(pid)
终止它时,临时文件将不会被删除。即使脚本被 process.kill
或 subProcess.kill
终止,如何确保删除临时文件?
注意:脚本在windows环境下运行时会出现这个问题(我使用的是Windows 11,通过git bash、powershell运行)。但是当脚本在linux上运行时不会出现这种情况。(我使用WSL2,ubuntu)。
我想这可能与此信号事件有关:
Windows 不支持信号,因此没有相当于通过信号终止的功能,但 Node.js 提供了一些对 process.kill() 和 subprocess.kill() 的模拟:
有什么解决办法吗?
另一个注意事项:这个脚本被传递给lint-staged,如果我没记错的话,当任务失败时,其他并发运行的任务将被process.kill杀死。我无法控制是否使用
process.kill
。
import fs from "fs";
const tmpFile = "foo.txt";
fs.writeFileSync(tmpFile, "randomContent");
// Attach cleanup handlers
let didCleanup = false;
for (const signal of ["exit", "SIGHUP", "SIGINT", "SIGTERM"] as const) {
process.on(signal, () => {
if (!didCleanup) {
didCleanup = true;
fs.unlinkSync(tmpFile);
}
if (signal !== "exit") {
let exitCode: number;
switch (signal) {
case "SIGHUP":
exitCode = 129;
break;
case "SIGINT":
exitCode = 130;
break;
case "SIGTERM":
exitCode = 143;
break;
}
process.exit(exitCode);
}
});
}
// some other tasks that interact with the temp file...
经过一番研究,这就是我解决问题的方法。
为了确保即使进程终止也执行清理,您可以创建一个守护进程。
从技术上来说,与类Unix系统相比,Windows对进程信号的支持有限,特别是当
process.kill
用于终止进程时,信号不会被进程捕获,因此即使你在这些信号上注册事件process.on("SIGINT")
,process.on("SIGTERM")
..,它不会激发听众。请参阅信号事件。
要在 NodeJS 中创建守护进程,可以使用 “double-forking”,以下是一个简化的示例,如果您需要更多详细信息,可以查看此包:tscw-config。
// main.js
if (process.platform === "win32") {
// Create a daemon by double-forking.
const intermediate = spawn(
process.argv[0],
[
path.relative(process.cwd(), path.join(__dirname, "./intermediate.js")),
// pass down all args that cleanup function needs.
process.pid.toString(),
tmpTsconfig,
],
{
detached: true,
stdio: "ignore",
},
);
intermediate.unref();
}
// intermediate.js
const parentPid = parseInt(process.argv[2], 10).toString();
const tmpTsconfig = process.argv[3];
// spawn the cleanup function then exit immediately, this will make the cleanup process an orphan process.
const sub = spawn(
process.argv[0],
[path.relative(process.cwd(), path.join(__dirname, "./cleanupMonitor.js")), parentPid, tmpTsconfig],
{
detached: true,
stdio: "ignore",
},
);
sub.unref();
exit(0);
// cleanupMonitor.js
const parentPid = parseInt(process.argv[2], 10);
const tmpTsconfig = process.argv[3];
// Check every second if the parent process is still running
setInterval(() => {
if (!isRunning(parentPid)) {
if (fs.existsSync(tmpTsconfig)) {
fs.unlinkSync(tmpTsconfig);
}
exit(0);
}
}, 1000);