最近读到一篇关于 CLI 的文章: Exploring CLI Best Practices, 关于开发 CLI 程序的一些最佳实践, 感觉非常受益. 文中提出14条建议, 挨个实践下来即可.
1. Every option that can have a default option should have a default option.
所有可以有默认值的选项都应该设置默认值.
但我觉得还值得补充的是: 默认值必须通过日志/print 方式告知用户. 不然有些选项默认值对新用户有种懵逼的效用.
2. Provide long, readable option names with short aliases.
提供可读性很好的长名字的选项名称和短的简写选项名称
例如, 在 python 中:
parser.add_argument('-v', '--version', help="print the current version")
文中的理由是: 长名字可读性好, 而简写方便用户在写脚本的时候方便. 我不是很同意提供简写选项.理由如下:
- 简写重名情况下, 有人倾向于选择一个根本与长名称选项不相关的简写, 很迷惑, 例如,
-v
可以用于--version
标识打印版本号, 但如果我还有一个选项--verbose
用户打开调试日志怎么办? 换一个其他的简写? - 不提供简写是避免有人用简写开发了脚本而其他人接手后还要继续查询 help 搞清楚什么含义.
3. Use common command line options.
用常用的选项名称
就是遵从大多数的常用单词, 别自己发明. 给出的链接也很有用, 值得参照.
4. Provide options for explicitly identifying the files to process.
提供选项支持指定要处理的文件
5. Don’t have positional options.
不要使用位置相关的选项
例如: your_command arg1 arg2 arg3
必须按照位置来的情况. 使用 --option option_value
的方式.
不过这个也特殊的情况, 比如一个命令包含了几个大模块的功能, 第一个选项用于指定子模块. 例如: airflow 就是第一个选项是模块名称, 每个模块下面都是通过 --option option-value
的方式指定参数.
6. Provide an extensive, comprehensive help command that can be accessed by help, --help or -h.
通过 -h 或者 --help 选项打印帮助文档.
7. Provide a version command that can be accessed by version, --version or -v.
通过 -v 或者 --version 打印版本号
但我觉得还不够, 应该在每次程序运行日志开始自动打印当前命令的版本号. 方便时候调试定位问题.
8. Don't go for a long period without output to the user.
长时间运行的程序, 通过打印一些信息告知用户程序没完蛋, 而是很忙
9. If a command has a side effect provide a dry-run/whatif/no_post option.
如果命令有副作用, 提供 dry-run 选项
dry-run 选项就是仅仅检测当前情况是否满足运行条件, 而不最终执行. dry-run 是我认为最性感的 CLI 的feature. 不光针对有副作用的命令, 针对需要非常长时间运行的命令也是如此(例如 Apache Pig 就有dry-run 特性, 而 Hive 就没有), 开发过程必定要调试命令是否ok.
实现 dry-run 不是简单的提供一个什么都不干的选项糊弄用户, 而是分几步走:
- 确定命令的核心功能, 比如 Pig 就是运行 pig 计算脚本
- 在执行核心功能前一步, 检测 dry-run, 如果打开, 程序就此停止, 告知用户.
- 在 dry-run 作用前, 检测所有的参数合法性, 执行环境是否OK.
再说一遍: dry-run 是方便用户大胆调试危险操作, 而不是糊弄用户. 要保证 dry-run 打开执行成功的情况下, 删掉dry-run 程序立马执行核心功能
10. For long running operations, allow the user to recover at a failure point if possible.
长时间运行的程序, 允许用户可以从上次失败的点恢复操作
这个说起来简单, 但做到不容易. 毕竟涉及到 checkpoint 的存储和恢复. 举一个容易实现的例子: maven 编译多模块的 Java 项目, 提供了 mvn 命令 -rf 模块名称
的方式, 从之前失败的模块继续执行.
11. Exit with nonzero status codes if and only if the program terminated with errors.
正常执行成功 exit_code 保证为0. 不正常情况保证 exit_code 非零
这是必须遵守的规范, 不遵守可以视为 bug. 是保证在shell 中通过 $? ==0
判断上一个命令是否成功的基础.
12. Write to stdout for useful information, stderr for warnings and errors.
正常信息输出到 stdout, 错误和警告信息输出到 stderr
13. Keep the CLI script itself as small as possible.
CLI 脚本本身越简单越好, 核心逻辑放到其他地方
14. Reserve outputting stack traces for truly exceptional cases.
保留错误情况下的堆栈信息
用 Java 的话说: 吃异常等于吃屎, 最好别干.
总结
非常好的一篇关于 CLI 开发的最佳实践, 虽然有几点我不是很赞同, 但还是值得仔细阅读, 认真遵守大多数.
-- EOF --