Node.js:小脚本解析命令行参数,先用 `util.parseArgs` 也够

24 次浏览4 条回复

Node 里写一次性脚本时,如果只是接几个 --name--dry-run 这种参数,不一定马上装 commander。Node 18+ 自带的 util.parseArgs 能先顶一下。

环境:Node.js 18+。保存成 parseargs-demo.mjs

import { parseArgs } from 'node:util'

const { values, positionals } = parseArgs({
  options: {
    name: { type: 'string', default: 'mesh' },
    count: { type: 'string', default: '1' },
    'dry-run': { type: 'boolean', default: false },
  },
  allowPositionals: true,
})

const count = Number(values.count)
if (!Number.isInteger(count) || count < 1) {
  throw new Error('--count 需要是正整数')
}

console.log({ values, positionals })

if (!values['dry-run']) {
  for (let i = 0; i < count; i++) {
    console.log(`${i + 1}: ${values.name}`)
  }
}

跑一下:

node parseargs-demo.mjs --name dev --count 2 input.txt
node parseargs-demo.mjs --dry-run input.txt

这个适合很轻的本地工具。参数开始变多、要子命令或者漂亮帮助信息时,再换专门的 CLI 库会舒服些。

这个适合小脚本。补一个细节:parseArgs 默认 strict: true,多传没声明的参数会直接抛错,其实挺适合 CI 里的脚本,能早点发现参数拼错。

如果是想把未知参数继续转给别的命令,再显式设 strict: false 会更清楚一点。

还有个小坑是 count 这种数字参数最后还是字符串,像楼主这样自己转一下更稳。

如果脚本稍微复杂一点,我一般会把参数解析和校验单独放到一个小函数里,后面主逻辑就别再到处读 values 了,改起来省心些。

-- 这个分隔符也可以顺手记一下。比如后面的位置参数可能长得像选项:

node parseargs-demo.mjs --name dev -- --not-an-option

这种情况下 --not-an-option 就不会再被当成参数名解析了。写包装脚本时挺常见,不然用户传文件名或下游参数时容易踩一下。

如果后面想自己做一点更细的处理,可以看看 tokens: true。它会把解析过程拆成 token 返回,比如 option、positional 这些,适合那种“标准参数先解析,剩下的再转交”的小包装脚本。

不过普通脚本就没必要一上来用它,valuespositionals 已经够直观了。