前端学习笔记 · 第 116 天:命令行运行脚本与 REPL
- 2026-11-01 15:47:54
前端学习笔记 · 第 116 天:命令行运行脚本与 REPL——参数、退出码与交互求值
Day 116 · 第五阶段 · Node.js 与后端开发
命令行的两个入口常被混用。node 文件 执行一整个脚本,跑完按退出码报告结果;直接输入 node 进入交互式求值,一行一行执行,结果立刻回显。把两者搞混,会遇到「为什么没有输出」「为什么上一行定义的变量不见了」这类问题。
一、问题现场
四个都来自真实的命令行误用:
把代码通过管道交给 node,期待看到结果回显,实际一片空白。管道输入按脚本执行,不是交互式环境,不会回显表达式。读参数时用 process.argv[0]当作脚本名,拿到的却是node的安装路径。脚本路径在第二个位置,或者根本没有。脚本已经因为缺少参数而失败,命令的退出码却是 0,CI 把这次失败当成成功放过去。 在交互环境里写 const a = 2,回显undefined,以为声明没有生效,于是重复定义。
四处都属于「入口与约定没对齐」,与代码逻辑无关。把入口、参数位置、退出码三件事弄清,后面写任何脚本都省事。
二、命令的组成与参数
一条 node 命令可以拆成四段:可执行文件、选项、脚本、参数。
node --max-old-space-size=128 args.mjs --port=3000 a.txt前两段由 node 自己消费,后两段进入脚本。脚本里通过 process.argv 读取,它是一个数组,位置固定:
process.argv[0]是 node可执行文件的完整路径。process.argv[1]是脚本路径;用 -e直接执行代码时,这个位置变成第一个参数。process.argv[2]起是其余参数,原样保留。
实测两种模式下的数组:
$ node -e "console.log(JSON.stringify(process.argv.slice(1)))" -- a b["a","b"]用 -e 执行时没有脚本路径,参数从下标 1 开始。用 -- 分隔时,-- 会被 node 消费掉,不会进入数组;而在脚本模式下,写在脚本名后面的 -- 属于参数,会原样保留。读取参数时先跳过单独的 --,可以同时兼容两种写法。
一次性求值有两个参数:
-e执行一段代码,不回显结果。 -p执行一段代码并打印结果,字符串直接输出,不带引号。
$ node -p "1+1"2$ node -p "'text'"text-e "1+1" 的实测输出为空,说明回显由 -p 负责,与执行本身无关。临时算一个值、检查一个环境变量,用 -p 比写文件快。
三、退出码
退出码是脚本与外部世界沟通结果的唯一标准渠道。约定很简单:0 表示正常结束,非 0 表示失败,具体数值由脚本自己决定。
设置退出码有两种写法,区别在时机:
process.exitCode = 1只是登记结果,当前同步代码继续执行,事件循环清空后进程才结束。 process.exit(2)立即结束进程,后面的代码不再执行。
实测两种写法的结果为 1 与 2。两者都不会自动把错误信息写出来,所以要配合 console.error 输出原因。错误写标准错误流、正常结果写标准输出,外部脚本可以分别重定向。
失败时给非零退出码还有一个直接作用:&& 串联的命令会在第一步失败时停下,CI 会把这次构建标记为失败。退出码为 0 的失败最难排查,因为所有环节都显示成功。
四、REPL 的三类输入
直接输入 node 回车,提示符是 >,进入交互式求值。这里能输入的内容分三类。
表达式。求值后立刻回显结果,_ 保存上一次的结果:
> 1 + 12> const a = 2undefined> a * 36> _6声明。声明语句本身没有值,回显 undefined 只表示「语句执行完了」,变量已经存在。下一行可以直接使用,a * 3 得到 6 就是证明。多行代码(函数、对象字面量)按回车后会自动续接,直到语句完整。
点命令。以点开头,控制会话本身,不参与求值:
> .help.break Sometimes you get stuck, this gets you out.clear Break, and also clear the local context.exit Exit the REPL.help Print this help message.load Load JS from a file into the REPL session.save Save all evaluated commands in this REPL session to a file六条命令各有分工:.break 从卡住的输入里退出来,.clear 清空上下文,.exit 结束会话,.load 把文件读进会话,.save 把会话写进文件。控制键同样可用,Ctrl+C 中断当前表达式,Ctrl+D 退出。
会话里 require 与 process 都可用,实测 typeof require 为 'function',typeof process 为 'object',process.version 读到 v24.12.0。所以临时验证一段依赖模块的代码,不一定要先建文件。
需要分清的一点:把代码通过管道交给 node,走的不是这条路径。实测 echo "1+1" | node 没有任何输出,_ 会报 ReferenceError,因为输入被当作脚本执行,而不是逐行求值。
五、实战示例:解析命令行参数
把前面的约定落成一个可以复用的脚本:读 process.argv.slice(2),把 --键=值 收进对象,其余当作文件,缺少必需项时给出用法并以非零退出码结束。
// args.mjs —— 解析命令行参数,缺少必需项时以非零退出码结束const argv = process.argv.slice(2);const flags = {};const files = [];for (const item of argv) { if (item === "--") continue; if (item.startsWith("--")) { const [key, value] = item.slice(2).split("="); flags[key] = value ?? true; } else { files.push(item); }}if (!flags.port) { console.error("缺少 --port,用法:node args.mjs --port=3000 文件…"); process.exitCode = 1;} else { console.log(`端口 ${flags.port}`); console.log(`文件 ${files.join("、") || "(未指定)"}`); console.log(`收到参数 ${Object.keys(flags).length} 个`);}正常调用时,输出三行:
$ node args.mjs --port=3000 a.txt b.txt端口 3000文件 a.txt、b.txt收到参数 1 个漏掉必需参数时,标准错误流里出现用法说明,退出码为 1:
$ node args.mjs a.txt缺少 --port,用法:node args.mjs --port=3000 文件…三处细节值得记住:slice(2) 跳过前两个位置;item === "--" 单独处理,避免把分隔符解析成空键;失败分支只设置 process.exitCode,让后续逻辑自然结束,而不是在半路强行退出。
常见问题
Q1:输入 node 之后粘贴代码,为什么没有任何回显?
先确认输入方式:交互式会话需要真实的终端输入,逐行回车。把代码通过管道交给 node 时,输入会被当成一个脚本文件执行,不回显表达式,_ 也不存在。用 -e 或 -p 一次性执行,同样不会进入交互模式。
Q2:process.argv[0] 为什么不是脚本名?
数组的第一个位置固定是 node 可执行文件,脚本路径在第二个位置。用 -e 执行代码时没有脚本路径,参数直接从下标 1 开始。需要脚本名时取 argv[1],并先判断当前是否为 -e 模式。
Q3:脚本已经报错,CI 却显示成功,是什么原因?
多数情况是退出码为 0。没有设置 process.exitCode 或调用 process.exit 时,即使打印了错误,进程仍以 0 结束。失败分支里补上非零退出码,同时把原因写到标准错误流。
Q4:交互环境里 const a = 2 回显 undefined,是不是没定义?
声明语句没有返回值,回显 undefined 表示语句执行完毕。下一行直接使用该变量即可;如果提示重复声明,说明名字已经在当前上下文里存在,可以用 .clear 清空上下文再写。
Q5:怎么把整个文件加载进交互环境?
用 .load 文件路径,它把文件内容读进会话,声明与函数都能在后续行使用。需要把当前会话保存成文件时用 .save,两个命令都在 .help 的列表里。
Q6:-e 与 -p 该用哪个?
看是否要结果。-e 只执行,输出多少由代码里的打印决定;-p 执行后把表达式的值打印出来,字符串不带引号。算一个值、检查一个环境变量时用 -p,需要在代码里写多步逻辑时用 -e 或直接建文件。
知识点总结
入口两种:
用
node 文件执行脚本,直接输入node进入交互式求值命令四段:
可执行文件、选项、脚本、参数,前两段由 node 消费
参数位置:
数组前两位固定,
argv[0]是 node 路径,argv[1]是脚本路径,argv[2]起是参数-e 模式:
没有脚本路径,参数从下标 1 开始,
--被 node 消费-e 与 -p:
前者只执行,后者执行并打印结果,字符串不带引号
退出码:
0 成功、非 0 失败;
process.exitCode登记结果,process.exit立即结束错误输出:
原因写标准错误流,正常结果写标准输出,两者可分别重定向
REPL 三类:
表达式回显结果,声明回显
undefined,点命令控制会话_ 变量:
保存上一次求值结果;管道输入不走交互模式,
_不存在
(本文为学习笔记整理)