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 对照。

Linux命令Node.jsLinux系统UbuntuUbuntu 22.04命令行2026/10/17

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 堆 OOM134(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。

十六、与同类运行时对照

能力nodedenobunpython3
版本node -vdeno --versionbun --versionpython3 -V
求值打印node -p "expr"deno eval(无隐式打印)bun -e(无隐式打印)python3 -c(无隐式打印)
语法检查node -c f.jsdeno check f.ts—python3 -m py_compile f.py
REPLnode -ideno replbun replpython3
内存上限--max-old-space-size--v8-flags=--max-old-space-size=N同 V8 flag资源限制层面
调试node --inspectdeno inspect(Chrome DevTools)bun --inspectpython3 -m pdb
watchnode --watchdeno task --watch / deno run --watchbun --watch—(第三方 watchgod)

node 的 -p 隐式打印是四者中最顺手的探针写法;--watch/--inspect 均为内置,无需额外装包,这是相对 python3 的明显优势。