node 命令详解与参数实测(Ubuntu 22.04)
在 Ubuntu 22.04(Node.js v20.20.2 / npm 10.8.2,NodeSource 20.x 源)逐项实测 node 全流程:版本与运行时信息、执行脚本与 shebang、-e/-p 求值、-c 语法检查、process.argv 与 -- 分隔符、ESM/CJS 模块加载、-r 预加载、REPL、--max-old-space-size 内存控制与 OOM、V8 选项与 NODE_OPTIONS、--inspect 调试、--watch 热重启、退出码与 stdin 管道实战,含常见报错排查与 deno/bun/python 对照。
node 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 node 的每一个常用命令与参数——版本与运行时信息(
--version/-p process.versions)、脚本执行(直接运行、shebang 直跑、stdin-)、求值求打印(-e/-p及多参数顺序语义)、语法检查(-c/--check)、参数传递(process.argv与--分隔符)、模块系统(ESM.mjs、动态import()、-r预加载、--input-type=module)、REPL(管道喂入与-i交互)、内存控制(--max-old-space-size、OOM 退出码 134)、V8 选项与NODE_OPTIONS、调试器(--inspect/--inspect-brk/node inspect)、--watch热重启与退出码约定,全部命令与回显已在 Ubuntu 22.04(Node.js v20.20.2)实测通过。
一、环境信息
本文所有命令均在以下环境实测,回显逐字照抄:
- 操作系统:Ubuntu 22.04.5 LTS(
PRETTY_NAME="Ubuntu 22.04.5 LTS") - 内核架构:x86_64
- Node.js:v20.20.2(NodeSource 20.x 软件源,deb 包
nodejs 20.20.2-1nodesource1) - npm:10.8.2(随 nodejs 包附带)
- 执行身份:root
- 演示目录:
/opt/demo(含 ok.js、bad.js、args.js、lib.mjs、app.mjs、preload.js、mem.js、server.js 等)
$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.5 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
$ uname -m
x86_64
$ node --version
v20.20.2
$ npm --version
10.8.2
$ which node && which npm
/usr/bin/node
/usr/bin/npm
$ dpkg -l nodejs | tail -1
ii nodejs 20.20.2-1nodesource1 amd64 Node.js event-based server-side javascript engine
$ apt-cache policy nodejs | head -8
nodejs:
Installed: 20.20.2-1nodesource1
Candidate: 20.20.2-1nodesource1
Version table:
*** 20.20.2-1nodesource1 600
500 https://deb.nodesource.com/node_20.x nodistro/main amd64 Packages
100 /var/lib/dpkg/status
💡 说明: NodeSource 源安装的 node 二进制落在
/usr/bin/node,同名nodejs包由该源提供(Ubuntu 自带源的 node 包优先级不同,混装会冲突,建议只留一路来源)。
二、命令概览
node [options] [ script.js ] [arguments]
node inspect [options] [ script.js | host:port ] [arguments]
| 用法 | 作用 | 本文小节 |
|---|---|---|
node --version / -v | 打印 Node.js 版本 | 三 |
node -p "表达式" | 求值并打印结果 | 五 |
node -e "代码" | 求值执行,不隐式打印 | 五 |
node -c file.js / --check | 只做语法检查不执行 | 六 |
node script.js args... | 执行脚本,参数进 process.argv | 七 |
node -- args.js --version | -- 后参数原样传给脚本 | 七 |
node --input-type=module -e "..." | 按模块(ESM)解析求值代码 | 八 |
node -r ./preload.js main.js | 执行前预加载模块 | 八 |
node -i | 强制进入交互 REPL | 九 |
node --max-old-space-size=N | 设置老生代堆上限(MB) | 十 |
node --v8-options | 打印全部 V8 选项 | 十一 |
NODE_OPTIONS="..." node ... | 经环境变量注入参数 | 十一 |
node --inspect [host:]port script | 启动 Inspector 调试服务 | 十二 |
node inspect script.js | 启动内置命令行调试器 | 十二 |
node --watch script.js | 文件变更自动重启 | 十三 |
node - | 从 stdin 读脚本 | 四/十四 |
三、版本与运行时信息
--version 与 -v 完全等价;想一次看全运行时构成,用 -p "process.versions":
$ node --version
v20.20.2
exit=0
$ node -v
v20.20.2
exit=0
$ node -p "process.versions"
{
node: '20.20.2',
acorn: '8.16.0',
ada: '2.9.2',
ares: '1.34.6',
brotli: '1.1.0',
cjs_module_lexer: '2.2.0',
cldr: '48.0',
icu: '78.2',
llhttp: '9.3.1',
modules: '115',
napi: '9',
nghttp2: '1.61.0',
openssl: '3.0.19',
simdutf: '6.4.2',
tz: '2025c',
undici: '6.24.1',
unicode: '17.0',
uv: '1.46.0',
uvwasi: '0.0.23',
v8: '11.3.244.8-node.38',
zlib: '1.3.1-e00f703'
}
$ node -p "process.version + \" / V8 \" + process.versions.v8 + \" / OpenSSL \" + process.versions.openssl"
v20.20.2 / V8 11.3.244.8-node.38 / OpenSSL 3.0.19
$ node -e "console.log(process.platform, process.arch)"
linux x64
💡 说明:
modules: '115'是 ABI 版本(NODE_MODULE_VERSION),排查原生扩展(如编译过的 sqlite3)与当前 node 是否兼容时看这个值;process.platform返回linux、process.arch返回x64,脚本内做平台分支判断比解析uname可靠。
四、执行脚本:直接运行、shebang 直跑与 stdin
三种喂脚本方式:文件参数、加执行位走 shebang、- 从 stdin 读。
$ node -e "console.log(\"hello from eval\")"
hello from eval
$ node app.mjs
sum(1,2) = 3, PI = 3.14159
$ node args.js alpha beta 42
argv = [ '/usr/bin/node', '/opt/demo/args.js', 'alpha', 'beta', '42' ]
argv[1] = /opt/demo/args.js
slice(2) = [ 'alpha', 'beta', '42' ]
$ ./args.js direct-run
bash: line 9: ./args.js: Permission denied
exit=126
文件没有执行位时报 Permission denied(退出码 126),要么 node args.js 调用,要么补执行位:
$ chmod +x args.js && ./args.js direct-run
argv = [ '/usr/bin/node', '/opt/demo/args.js', 'direct-run' ]
argv[1] = /opt/demo/args.js
slice(2) = [ 'direct-run' ]
shebang 行 #!/usr/bin/env node 让内核交给 env 找到 node 解释器,脚本即可当普通可执行文件用。
- 表示脚本来自 stdin(管道/重定向):
$ cat hello.js
console.log("hello, node");
$ cat hello.js | node -
hello, node
$ node - < /tmp/s.js
from stdin script
argv: [ '-' ]
💡 说明:
node -时process.argv[1]是'-',脚本里不要再拿 argv[1] 当文件名用。
五、-e 与 -p:求值执行 vs 求值打印
两者都把参数当代码求值,区别只在是否隐式 console.log:
$ node -e "console.log(2 ** 10)"
1024
$ node -p "Date.now() > 0"
true
$ node -p "[1,2,3].map(n => n * n)"
[ 1, 4, 9 ]
$ node -e "console.log(\"A\")" -p "1 + 1" -e "console.log(\"B\")"
B
undefined
最后一个注意点:多段 -e/-p 只有最后一段生效(前者被覆盖),且混合时求值顺序按给定次序、仅打印最后一段的值:
$ node -p "1 + 1" -e "console.log(\"last wins\")"
last wins
undefined
$ node -e "console.log(\"first\")" -p "2 + 2"
4
$ node -e "console.log(\"first\")" -e "console.log(\"second\")"
second
-e 段里抛错同样按未捕获异常处理,退出码 1:
$ node -e "JSON.parse(\"{bad json}\")"
<anonymous_script>:1
{bad json}
^
SyntaxError: Expected property name or '}' in JSON at position 1
at JSON.parse (<anonymous>)
at [eval]:1:6
at runScriptInThisContext (node:internal/vm:209:10)
at node:internal/process/execution:118:14
at [eval]-wrapper:6:24
at node:internal/main/eval_string:51:3
Node.js v20.20.2
exit=1
💡 说明: 想在 shell 单引号里写含引号的 JS,
-p短小适合取值/探针(node -p "process.env.PATH"),-e适合有副作用的多行逻辑。
六、-c / --check:语法检查不执行
-c 是 --check 的短写,只解析不运行,CI 里做 lint 前置很合适:
$ node -c ok.js
exit=0
$ node --check ok.js
exit=0
$ cat semicolon.js && node -c semicolon.js
function add(a, b) {
return a + b
}
console.log(add(2, 3));
exit=0
$ node -c bad.js
/opt/demo/bad.js:2
return a ++ b;
^
SyntaxError: Unexpected identifier 'b'
at wrapSafe (node:internal/modules/cjs/loader:1464:18)
at checkSyntax (node:internal/main/check_syntax:78:3)
Node.js v20.20.2
exit=1
$ node bad.js
/opt/demo/bad.js:2
return a ++ b;
^
SyntaxError: Unexpected identifier 'b'
at wrapSafe (node:internal/modules/cjs/loader:1464:18)
at Module._compile (node:internal/modules/cjs/loader:1623:10)
at Module.load (node:internal/modules/cjs/loader:1266:32)
at Module._load (node:internal/modules/cjs/loader:1091:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/main/run_main_module:28:49)
exit=1
语法错误时 -c 与直接执行报同样的 SyntaxError、退出码都是 1,差别是 -c 走 check_syntax 路径、绝不触发副作用。批量检查:
$ for f in ok.js bad.js lib.mjs; do node -c "$f" >/dev/null 2>&1 && echo "$f: syntax OK" || echo "$f: syntax FAIL"; done
ok.js: syntax OK
bad.js: syntax FAIL
lib.mjs: syntax OK
七、process.argv 与 -- 分隔符
process.argv 前两位固定是 node 路径与脚本路径,业务参数从 slice(2) 取;node 自身的选项想在参数区出现需用 -- 显式分隔:
$ node -- args.js --version
argv = [ '/usr/bin/node', '/opt/demo/args.js', '--version' ]
argv[1] = /opt/demo/args.js
slice(2) = [ '--version' ]
没有 -- 时 --version 会被 node 自己消费(打印版本后退出)。-e 模式下脚本区为空,额外参数从 argv.slice(1) 开始:
$ node -e "console.log(process.argv.slice(1))" extra1 extra2
[ 'extra1', 'extra2' ]
八、模块系统:ESM、动态 import 与 -r 预加载
.mjs 后缀按 ES Module 解析,import 在顶层可用;对 -e 代码需用 --input-type=module 声明模块语义:
$ cat lib.mjs
export const sum = (a, b) => a + b;
export const PI = 3.14159;
$ cat app.mjs
import { sum, PI } from "./lib.mjs";
console.log(`sum(1,2) = ${sum(1, 2)}, PI = ${PI}`);
$ node app.mjs
sum(1,2) = 3, PI = 3.14159
$ node --input-type=module -e "import { readdirSync } from \"node:fs\"; console.log(readdirSync(\".\").join(\", \"))"
app.mjs, args.js, bad.js, lib.mjs, ok.js, semicolon.js
exit=0
$ node --input-type=module -e "await Promise.resolve(); console.log(\"top-level await ok\")"
top-level await ok
--input-type=module 下顶层 await 合法;CJS 求值里 await 表达式同样返回值(内部按 promise 求值),但这不等于顶层 await 语义,跨 await 的代码请走模块模式。
-r(--require)在主逻辑执行前预加载模块,常用来打点、改全局、注入探针:
$ cat preload.js
console.log("[preload] loaded, NODE_ENV =", process.env.NODE_ENV);
global.__bootAt = Date.now();
$ cat main.js
console.log("[main] preload set __bootAt =", global.__bootAt);
$ NODE_ENV=production node -r ./preload.js main.js
[preload] loaded, NODE_ENV = production
[main] preload set __bootAt = 1792202699756
$ node --require ./preload.js main.js
[preload] loaded, NODE_ENV = undefined
[main] preload set __bootAt = 1792202699777
$ node -r ./preload.js -e "console.log(\"eval body, __bootAt =\", __bootAt)"
[preload] loaded, NODE_ENV = undefined
eval body, __bootAt = 1792202699799
$ node -r ./no-such.js main.js
node:internal/modules/cjs/loader:1210
throw err;
^
Error: Cannot find module './no-such.js'
Require stack:
- internal/preload
at Module._resolveFilename (node:internal/modules/cjs/loader:1207:15)
at Module._load (node:internal/modules/cjs/loader:1038:27)
at internalRequire (node:internal/modules/cjs/loader:219:19)
at Module._preloadModules (node:internal/modules/cjs/loader:1785:5)
at loadPreloadModules (node:internal/process/pre_execution:745:5)
at setupUserModules (node:internal/process/pre_execution:206:5)
at prepareExecution (node:internal/process/pre_execution:53:10)
at node:internal/main/run_main_module:11:19 {
code: 'MODULE_NOT_FOUND',
requireStack: [ 'internal/preload' ]
}
Node.js v20.20.2
exit=1
预加载模块找不到直接 MODULE_NOT_FOUND(退出码 1),进程不会带病启动。ESM 场景没有 -r 对应物,用 import './preload.js' 顶置一行替代。
九、REPL:管道喂入与 -i 交互
stdin 是管道且非 tty 时,node 把输入当脚本执行;-i 强制按 REPL 逐行求值,.exit 或 Ctrl-D 退出:
$ echo "1 + 1" | node
exit=0
$ printf "console.log(2 ** 8)\n" | node
256
$ printf "1 + 1\nprocess.version\n.exit\n" | node -i
Welcome to Node.js v20.20.2.
Type ".help" for more information.
> 2
> 'v20.20.2'
>
$ printf "const arr = [1,2,3]\narr.map(n => n * 2)\n.exit\n" | node -i
Welcome to Node.js v20.20.2.
Type ".help" for more information.
> undefined
> [ 2, 4, 6 ]
>
💡 说明:
-i模式下每行是一个求值单元,声明语句(const)回显undefined、表达式回显值;无-i时整段按脚本编译执行,行为一致但不再逐行回显。
十、内存控制:--max-old-space-size 与 OOM
默认老生代堆上限约 4 GB(64 位平台按内存推导)。--max-old-space-size 单位 MB:
$ node -p "require(\"v8\").getHeapStatistics().heap_size_limit / 1024 / 1024 + \" MB\""
4144 MB
$ node --max-old-space-size=256 -p "require(\"v8\").getHeapStatistics().heap_size_limit / 1024 / 1024 + \" MB\""
304 MB
$ node --max-old-space-size=512 -p "require(\"v8\").getHeapStatistics().heap_size_limit / 1024 / 1024 + \" MB\""
560 MB
读到的大于设定值是正常的——heap_size_limit 还包含新生态等保留区。超限后 V8 反复 Full GC 仍腾不出空间就抛 OOM:
$ node --max-old-space-size=256 mem2.js
bash: line 4: 3885 Aborted node --max-old-space-size=256 mem2.js > /tmp/mo.txt 2>&1
exit=134
<--- Last few GCs --->
[3885:0x3341acb0] 209 ms: Mark-Compact (reduce) 258.5 (263.2) -> 258.4 (260.2) MB, 1.16 / 0.00 ms (+ 0.3 ms in 1 steps since start of marking, biggest step 0.3 ms, walltime since start of marking 5 ms) (average mu = 0.913, current mu = 0.845) finali
<--- JS stacktrace --->
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
----- Native stack trace -----
1: 0xb78db3 node::OOMErrorHandler(char const*, v8::OOMDetails const&) [node]
2: 0xee8300 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [node]
--max-old-space-size=1024 时同样在约 1026 MB 处触发,说明该参数就是硬上限;把申请量压回限额内则正常退出:
$ node --max-old-space-size=512 mem3.js 40
allocated: 40 chunks, 320 MB
exit=0
$ node --max-old-space-size=256 mem3.js 20(NODE_OPTIONS 等价写法见下节)
allocated: 20 chunks, 160 MB
exit=0
💡 说明: OOM 退出码是 134(128+6,SIGABRT),k8s/系统 detecting 内存问题是看到 134 先查这一段
FATAL ERROR: Reached heap limit,别和进程被 OOM killer 击杀(137,SIGKILL)混淆。
十一、V8 选项与 NODE_OPTIONS
--v8-options 打印全部底层 V8 flag(不含 node 自己的选项):
$ node --v8-options | grep -cE "^ --"
687
$ node --v8-options | grep -E "^ --(use-strict|stack-size)" -A1
--use-strict (enforce strict mode)
type: bool default: --no-use-strict
--
--stack-size (default size of stack region v8 is allowed to use (in kBytes))
type: int default: --stack-size=984
$ node --v8-options --version
v20.20.2
--v8-options 只打印选项不执行代码,与 --version 连用时后者胜出。
NODE_OPTIONS 把参数经环境变量注入,适合不改启动命令就调参数(systemd、npm scripts 场景):
$ NODE_OPTIONS="--max-old-space-size=512" node -p "require(\"v8\").getHeapStatistics().heap_size_limit / 1024 / 1024 + \" MB\""
560 MB
$ NODE_OPTIONS="--max-old-space-size=512 --trace-warnings" node -p "1+1"
2
$ NODE_OPTIONS="-p \"process.version\"" node
node: -p is not allowed in NODE_OPTIONS
exit=9
$ NODE_OPTIONS="--no-such-flag" node -p "1+1"
node: --no-such-flag is not allowed in NODE_OPTIONS
exit=9
安全约束:-p、-e、--inspect 等可执行代码/开放端口的选项禁止出现在 NODE_OPTIONS,未知参数同样直接拒绝启动,退出码 9。命令行等价写法:
$ node --max-old-space-size=512 -p "1+1"
2
十二、调试:--inspect 与 node inspect
--inspect 在本机 9229 起一个 Inspector WebSocket 服务,--inspect-brk 额外在首行断住;node inspect(两个词)是内置命令行调试器,自动连接调试服务:
$ timeout 3 node --inspect server.js 2>&1 | head -6
Debugger listening on ws://127.0.0.1:9229/a0e4ef84-9a5e-495a-bcd2-c3aca87be301
For help, see: https://nodejs.org/en/docs/inspector
listening on 3000
$ timeout 3 node --inspect-brk server.js 2>&1 | head -6
Debugger listening on ws://127.0.0.1:9229/d3740b9e-2aaf-4cf3-a921-243c3ac93a6d
For help, see: https://nodejs.org/en/docs/inspector
$ timeout 3 node --inspect=0.0.0.0:9230 server.js 2>&1 | head -6
Debugger listening on ws://0.0.0.0:9230/a79a9536-261c-4b96-b914-502870189a99
For help, see: https://nodejs.org/en/docs/inspector
listening on 3000
--inspect=host:port 可改监听地址;调试 HTTP 端点 http://127.0.0.1:9229/json/list 返回目标列表:
$ node --inspect=0.0.0.0:9229 server.js > /tmp/insp.log 2>&1 & sleep 1; cat /tmp/insp.log
Debugger listening on ws://0.0.0.0:9229/17c32005-1cff-41a9-9cc2-7e96de29d305
For help, see: https://nodejs.org/en/docs/inspector
listening on 3000
$ node -e "require(\"http\").get(\"http://127.0.0.1:9229/json/list\", r => { let d = \"\"; r.on(\"data\", c => d += c); r.on(\"end\", () => console.log(JSON.stringify(JSON.parse(d), null, 2))); })"
[
{
"description": "node.js instance",
"devtoolsFrontendUrl": "devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=127.0.0.1:9229/17c32005-1cff-41a9-9cc2-7e96de29d305",
"devtoolsFrontendUrlCompat": "devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/17c32005-1cff-41a9-9cc2-7e96de29d305",
"faviconUrl": "https://nodejs.org/static/images/favicons/favicon.ico",
"id": "17c32005-1cff-41a9-9cc2-7e96de29d305",
"title": "server.js",
"type": "node",
"url": "file:///opt/demo/server.js",
"webSocketDebuggerUrl": "ws://127.0.0.1:9229/17c32005-1cff-41a9-9cc2-7e96de29d305"
}
]
$ node -e "require(\"http\").get(\"http://127.0.0.1:3000\", r => { let d = \"\"; r.on(\"data\", c => d += c); r.on(\"end\", () => process.stdout.write(d)); })"
hello inspector
命令行调试器 node inspect 的会话交互(stdin 喂 list(4) 后退出):
$ ( sleep 2; echo "list(4)"; sleep 1; echo "quit" ) | timeout 15 node inspect args.js
< Debugger listening on ws://127.0.0.1:9229/86f5eca4-9e05-4622-8027-cc938bca0576
< For help, see: https://nodejs.org/en/docs/inspector
<
connecting to 127.0.0.1:9229 ... ok
< Debugger attached.
<
debug>Break on start in args.js:2
1 #!/usr/bin/env node
> 2 console.log("argv =", process.argv);
3 console.log("argv[1] =", process.argv[1]);
4 console.log("slice(2) =", process.argv.slice(2));
debug> 1 #!/usr/bin/env node
> 2 console.log("argv =", process.argv);
3 console.log("argv[1] =", process.argv[1]);
4 console.log("slice(2) =", process.argv.slice(2));
5
debug> REPL2:1
quit
^
Uncaught ReferenceError: quit is not defined
at REPL2:1:1
at Script.runInContext (node:vm:149:12)
at Object.runInContext (node:vm:301:6)
at REPLServer.controlEval (node:internal/debugger/inspect_repl:626:25)
at bound (node:domain:444:12)
at [_onLine] [as _onLine] [as _onLine] (node:internal/readline/interface:419:12)
debug> exit=0
💡 说明:
node inspect默认--inspect-brk语义(首行断住),n单步、c继续、sb(行号)下断点;调试器里quit不是命令(那是 REPL 求值),正确做法 Ctrl-C 两次或.exit。远程调试务必绑定内网地址并配防火墙,0.0.0.0:9229等于把代码执行权暴露在网段里。
十三、--watch 与退出码
--watch 监视入口文件与依赖,变更后自动重启;脚本正常退出打印 Completed running,崩溃打印 Failed running,修复后仍会自动重启:
$ printf "console.log(1)\nsetTimeout(()=>console.log(2),100)\n" > w.js && timeout 5 node --watch w.js & sleep 2 && printf "console.log(1)\nsetTimeout(()=>console.log(2),100)\nconsole.log(3)\n" > w.js; wait
1
2
Completed running 'w.js'
$ node --watch 重启演示(追加一行触发重启)
1
Completed running 'w2.js'
$ node --watch:脚本崩溃 → 修复 → 自动重启
/opt/demo/w3.js:1
throw new Error('v1 crashed');
^
Error: v1 crashed
at Object.<anonymous> (/opt/demo/w3.js:1:7)
at Module._compile (node:internal/modules/cjs/loader:1521:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1623:10)
at Module.load (node:internal/modules/cjs/loader:1266:32)
at Module._load (node:internal/modules/cjs/loader:1091:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/main/run_main_module:28:49)
Node.js v20.20.2
Failed running 'w3.js'
watch 目录文件过多会碰到 EMFILE(fs.watch 的描述符上限):
$ fs.watch 事件探测(/opt/demo,3 秒)
fs.watch error: EMFILE: too many open files, watch '/opt/demo'
退出码约定实测:
$ node -e "process.exit(7)"; echo $?
7
$ node -e "null"
exit=0
$ node -e "throw new Error(\"boom\")"
[eval]:1
throw new Error('boom')
^
Error: boom
at [eval]:1:7
at runScriptInThisContext (node:internal/vm:209:10)
at node:internal/process/execution:118:14
at [eval]-wrapper:6:24
at evalScript (node:internal/main/eval_string:51:3)
Node.js v20.20.2
exit=1
| 场景 | 退出码 |
|---|---|
| 正常结束 | 0 |
process.exit(n) | n |
| 未捕获异常 / 语法错误 | 1 |
NODE_OPTIONS 非法参数 | 9 |
| 执行位缺失(shell 层) | 126 |
| V8 堆 OOM | 134(SIGABRT) |
十四、stdin 管道实战:readline 处理 JSONL
配合 node:readline 可以把 node 当流式文本处理器用(逐行 JSON 求和):
$ cat events.jsonl
{"host":"web1","rt":12}
{"host":"web2","rt":35}
{"host":"web3","rt":18}
$ cat events.jsonl | node -e "const readline = require(\"node:readline\"); let n = 0, sum = 0; readline.createInterface({ input: process.stdin }).on(\"line\", line => { if (!line.trim()) return; const o = JSON.parse(line); n++; sum += o.rt; }).on(\"close\", () => console.log(\"records =\", n, \", avg rt =\", (sum / n).toFixed(1)));"
records = 3 , avg rt = 21.7
on("line") 逐行回调 + on("close") 收口,天然背压友好;比「整段读入再 split」省内存,适合日志管道。
十五、常见问题与排查
1. Permission denied 退出码 126:脚本没有执行位或 shebang 缺失。要么 node script.js,要么 chmod +x 并确认首行 #!/usr/bin/env node。
2. Cannot find module './x.js'(含 -r 预加载):路径按相对 CWD 或模块自身解析,-r 的相对路径以启动时 CWD 为准;requireStack: [ 'internal/preload' ] 说明是预加载阶段没找到。
3. SyntaxError 但代码看起来没问题:先 node -c file.js 定位行号;ESM 语法(import/顶层 await)跑在 .js(CJS 默认)下会报错,改 .mjs 或加 --input-type=module。
4. node: xxx is not allowed in NODE_OPTIONS(exit=9):该选项不允许经环境变量注入(-p/-e/--inspect 等),只能写进命令行。
5. exit=134 + Reached heap limit:JS 堆超限,调大 --max-old-space-size 或排查泄漏;对照被系统 OOM killer 杀掉是 137。
6. EMFILE: too many open files, watch '...':监视目录条目超描述符上限,收窄监视范围或调大 ulimit -n,大目录改用 chokidar 这类增量方案。
7. --version 出现在脚本参数里被 node 消费:用 -- 分隔,node -- args.js --version,-- 后参数原样进 process.argv.slice(2)。
8. 多段 -e/-p 行为不符预期:只有最后一段生效,前者被覆盖;需要多步逻辑合并进一段 -e。
十六、与同类运行时对照
| 能力 | node | deno | bun | python3 |
|---|---|---|---|---|
| 版本 | node -v | deno --version | bun --version | python3 -V |
| 求值打印 | node -p "expr" | deno eval(无隐式打印) | bun -e(无隐式打印) | python3 -c(无隐式打印) |
| 语法检查 | node -c f.js | deno check f.ts | — | python3 -m py_compile f.py |
| REPL | node -i | deno repl | bun repl | python3 |
| 内存上限 | --max-old-space-size | --v8-flags=--max-old-space-size=N | 同 V8 flag | 资源限制层面 |
| 调试 | node --inspect | deno inspect(Chrome DevTools) | bun --inspect | python3 -m pdb |
| watch | node --watch | deno task --watch / deno run --watch | bun --watch | —(第三方 watchgod) |
node 的 -p 隐式打印是四者中最顺手的探针写法;--watch/--inspect 均为内置,无需额外装包,这是相对 python3 的明显优势。