← 中文资源

简体中文版本

构建带校验与安全默认值的交互式命令行提示

只在参数缺失时提问、在循环内校验答案,并让非交互模式成为永远可用的逃生门。

阅读英文原文

目标

交互式命令行对人类很友好,遇到管道就变成灾难。能同时活在两个世界的架构是:旗标是契约,提示只补缺失值,校验在提问循环里就地完成,再加一道 TTY 检测把“无限挂起”变成一条可行动的报错。CI 里两秒钟退出,人在终端里被友好地问。

步骤要点

  1. 先检测 stdin.isTTY,非终端环境且必填旗标缺失时打印“在脚本与 CI 中请显式传 –name”并以退出码 2 结束。
  2. 实现 ask 循环:问题带可见默认值后缀,空回车取默认,validate 回调返回错误文案就打印“invalid: 原因”并重新提问,finally 关闭 readline 保证 Ctrl-C 干净退出。
  3. 接线时让旗标驱动一切:args.name 存在就不问,region 有默认永不问,确认步骤对交互场景开放且支持 –yes 旁路。
  4. 用管道实测非交互路径:echo 空串加旗标能跑通,/dev/null 无旗标时两秒内报错退出而非挂起。
  5. 给提问循环本身加脚本化端到端检查:通过 stdin 先送非法值再送合法值,断言重试提示出现且最终成功,无需真人键盘。

排错提示

CI 里挂起是 TTY 检查缺失或做得太晚,上线前用 < /dev/null 自测,stdin 非终端且有必填旗标未给就提前退出;带空格引号的答案炸脚本说明用了裸 read,shell 用 read -r、Node 用 readline,插值全部加引号并先做形状校验;破坏性确认被手滑 y 过关就改成“输入资源名以确认”,默认值只留给安全操作,销毁类提示默认中止。

适用边界

交互提示只属于人类,每多一个问题脚本就多一个答不了的空,旗标才是真正的契约;输入资源名确认也会被录屏与粘贴绕过,硬删除要靠显式 id 而不只是一个词。

代码示例

interactive session:
  $ node cli.mjs
  project name (demo-app): BAD NAME!
    invalid: lowercase letters, digits and dashes only
  project name (demo-app): demo-app
  create demo-app in us-east-1? (yes): yes
  created.

non-interactive session (CI):
  $ node cli.mjs < /dev/null
  error: --name is required when stdin is not a TTY
  pass --name explicitly in scripts and CI
  exit=2   (2 seconds, not a hang)