Appearance
Node.js运行时
Node.js 运行脚本时需要和进程、标准输出、退出码、环境变量交互,运行时部分重点放在这些命令行行为上。
一、版本和入口
生产环境一般只跟 LTS。Current 版本适合试新特性,长期服务跟着 Current 走,升级时更容易被原生依赖绊一下。
bash
node -v # 查看 Node.js 版本
npm -v # npm 随 Node.js 一起安装
node -p "1+1" # -p 会打印表达式结果,排查环境时很方便一个脚本入口文件通常叫 index.js、main.js 或者按用途命名。
js
// index.js
console.log("script started");bash
node index.jsNode.js 从入口文件开始执行,再按 import 或 require 继续加载其它文件。小脚本入口名我倾向按用途起,比如 check-service.js,以后翻目录时更容易想起来它是干什么的。
二、process 对象
process 表示当前 Node.js 进程。
js
console.log(process.version); // Node.js 版本
console.log(process.platform); // 当前系统平台,比如 linux、win32
console.log(process.arch); // CPU 架构,比如 x64、arm64
console.log(process.pid); // 当前进程 ID当前工作目录用 process.cwd()。
js
console.log(process.cwd()); // 定时任务和 systemd 里常看这个,路径问题多半从这里查这个值不等于脚本文件所在目录。systemd、crontab、CI 里最容易因为工作目录不同读错相对路径。
三、命令行参数
命令行参数在 process.argv 里。
js
// args.js
console.log(process.argv);bash
node args.js nginx active前两个元素固定是 node 路径和脚本路径,业务参数从第 3 个开始取。
js
const [, , service, status] = process.argv; // 跳过前两个固定参数
if (!service || !status) {
console.error("usage: node check.js <service> <status>");
process.exit(1); // 参数缺失时明确失败,方便外部调度系统识别
}
console.log(`${service}\t${status}`);参数不超过两三个时,读 process.argv 就够。参数一多就换 commander 或 yargs,不然手写解析很快会把脚本主体挤乱。
四、标准输出和错误输出
正常结果走 stdout,错误信息走 stderr。
js
console.log("service active"); // stdout
console.error("service inactive"); // stderrLinux 里可以分开重定向。
bash
node check.js > result.log 2> error.log脚本给其它工具调用时,这个习惯很重要。stdout 放结果,stderr 放报错,后面接管道或收日志都省事。
js
const result = {
service: "nginx",
active: true
};
console.log(JSON.stringify(result)); // stdout 只放最终结果,方便 jq 继续处理五、退出码
退出码 0 表示成功,非 0 表示失败。
js
const active = false;
if (!active) {
console.error("nginx is inactive");
process.exit(2); // 可以用不同退出码区分失败类型
}
console.log("nginx is active");入口处统一兜底异常。
js
function main() {
throw new Error("check failed");
}
try {
main();
} catch (err) {
console.error(err.message); // 普通脚本输出里只保留可读错误
process.exit(1);
}CI、crontab、systemd 都会看退出码。只打印错误但返回 0,后续会被误判成功。
六、环境变量
环境变量从 process.env 里读,读出来永远是字符串或 undefined。
js
const port = Number(process.env.PORT || 3000); // 环境变量没有时给默认值
const logLevel = process.env.LOG_LEVEL || "info";
console.log({ port, logLevel });必填变量要检查。
js
function requireEnv(name) {
const value = process.env[name];
if (!value) {
throw new Error(`${name} is required`); // 缺配置时尽早失败
}
return value;
}
const token = requireEnv("API_TOKEN");
console.log(`token length=${token.length}`);Linux 里临时传变量:
bash
PORT=8080 LOG_LEVEL=debug node server.jsPowerShell 里是另一套写法:
powershell
$env:PORT=8080
$env:LOG_LEVEL="debug"
node server.js密钥不进代码仓库。.env.example 可以提交,真实 .env 放进 .gitignore。内部工具也按这个习惯来,虽说前期麻烦一点,但迁到 CI 或容器时不用再补债。
七、当前脚本路径
ESM 里没有 __dirname,需要从 import.meta.url 转。
js
import { fileURLToPath } from "node:url";
import path from "node:path";
const currentFile = fileURLToPath(import.meta.url); // 当前 JS 文件的绝对路径
const currentDir = path.dirname(currentFile); // 当前 JS 文件所在目录
console.log(currentFile);
console.log(currentDir);读取随脚本一起发布的模板文件时,用脚本目录更稳。
js
import { fileURLToPath } from "node:url";
import path from "node:path";
const currentDir = path.dirname(fileURLToPath(import.meta.url));
const templateFile = path.join(currentDir, "templates", "unit.service");
console.log(templateFile); // 不受执行命令时所在目录影响八、信号处理
长期运行的服务会收到系统信号。Docker、Kubernetes、systemd 停服务时常见的是 SIGTERM。
js
process.on("SIGTERM", () => {
console.log("received SIGTERM");
// 这里放关闭数据库连接、停止接收请求、保存状态等清理逻辑
process.exit(0);
});脚本类程序通常不写信号处理。HTTP 服务、定时调度器、常驻 Worker 才需要补,尤其是容器里要处理 SIGTERM,否则发布时旧连接可能拖住退出。
九、一个标准脚本骨架
CLI 或巡检脚本可以先用这个骨架。
js
async function main() {
const [, , service] = process.argv;
if (!service) {
throw new Error("usage: node check.js <service>");
}
console.log(`checking ${service}`);
}
main().catch((err) => {
console.error(err.message); // 统一错误出口
process.exit(1); // 保证失败时退出码非 0
});