【译】写个好的 CLI 程序
写个好的 CLI 程序
- Write a Good CLI Program 译文
命令行接口程序(CLI)是运行在终端的,这也就意味着没有图形界面,即 none-GUI
事实上,我们每天都会使用 CLI,例如 ls
、ps
、top
等。有一个很棒的 cli 应用列表,它收集了很多优秀的 CLI 程序。你可以看一下。我推荐 exa,它是一个用 Rust 编写的 ls
程序。
CLI 程序
CLI 程序使用起来跟下面类似:
$ ./program_name [arguments] [flags] [options]
一般我们可以通过 -h
和 --help
参数看到相关信息。
以 cargo
程序为例:
$ cargo -h
Rust's package manager
USAGE:
cargo [OPTIONS] [SUBCOMMAND]
OPTIONS:
-V, --version Print version info and exit
--list List installed commands
--explain <CODE> Run `rustc --explain CODE`
-v, --verbose Use verbose output (-vv very verbose/build.rs output)
-q, --quiet No output printed to stdout
--color <WHEN> Coloring: auto, always, never
--frozen Require Cargo.lock and cache are up to date
--locked Require Cargo.lock is up to date
-Z <FLAG>... Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
-h, --help Prints help information
Some common cargo commands are (see all commands with --list):
build Compile the current project
check Analyze the current project and report errors, but don't build object files
clean Remove the target directory
doc Build this project's and its dependencies' documentation
new Create a new cargo project
init Create a new cargo project in an existing directory
run Build and execute src/main.rs
test Run the tests
bench Run the benchmarks
update Update dependencies listed in Cargo.lock
search Search registry for crates
publish Package and upload this project to the registry
install Install a Rust binary
uninstall Uninstall a Rust binary
See 'cargo help <command>' for more information on a specific command.
现在你应该知道怎样去使用 cargo
了。
创建项目
我们以创建一个新的 CLI 项目开始!
这里我将其命名为 meow
$ cargo new meow
$ cd meow
cargo
会帮助你完成新项目的创建。
参数
我们已经看到 CLI 程序的样子,它通常有一些参数。
一个简单直接的方案是:
// main.rs
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
println!("{:?}", args);
}
$./meow a1 a2 a3
["meow", "a1", "a2", "a3"]
我们获取到所有的 CLI 参数。
然而,说实话,实际的 CLI 程序的参数会更复杂些,比如:
$ ./foo -g -e a1 a3 a4
$ ./foo a1 -e -l --path=~/test/123
因而,那个简单的方法就不太便于使用,原因如下:
- 参数可能有默认值
- 标识会有不同的顺序
- 一些选项的顺序会不同
- 参数
arg1
可能会绑定参数arg2
所以,我们需要一个 crate 帮助我们简化这个工作。
Clap
Clap 具备我们所需的功能,它是 Rust 实现的命令行参数快速解析工具。
怎么使用它呢?
首先,创建一个 cli.yml
文件,用于参数配置。它内容类似于下面:
<!-- cli.yml -->
name: myapp
version: "1.0"
author: Kevin K. <kbknapp@gmail.com>
about: Does awesome things
args:
- config:
short: c
long: config
value_name: FILE
help: Sets a custom config file
takes_value: true
- INPUT:
help: Sets the input file to use
required: true
index: 1
- verbose:
short: v
multiple: true
help: Sets the level of verbosity
subcommands:
- test:
about: controls testing features
version: "1.3"
author: Someone E. <someone_else@other.com>
args:
- debug:
short: d
help: print debug information
接着,我们在 main.rs
增加以下代码:
#[macro_use]
extern crate clap;
use clap::App;
fn main() {
// YAML 文件以相对于当前的入口文件被检索到,类似于 Rust 中的模块查找
let yaml = load_yaml!("cli.yml");
let m = App::from_yaml(yaml).get_matches();
match m.value_of("argument1") {
// ...
}
// ...
}
clap
crate 会加载并解析 yml 文件,然后我们可以在程序中使用解析出的参数。
有了上方的 cli.yml
文件,我们通过 -h
参数运行程序就会得到下面的结果:
$ meow -h
My Super Program 1.0
Kevin K. <kbknapp@gmail.com>
Does awesome things
USAGE:
MyApp [FLAGS] [OPTIONS] <INPUT> [SUBCOMMAND]
FLAGS:
-h, --help Prints help information
-v Sets the level of verbosity
-V, --version Prints version information
OPTIONS:
-c, --config <FILE> Sets a custom config file
ARGS:
INPUT The input file to use
SUBCOMMANDS:
help Prints this message or the help of the given subcommand(s)
test Controls testing features
非常方便,对吧?
配置
和大多数程序一样, CLI 程序也需要配置。一些参数应该在运行前确定,并记录在譬如 .env
、.config
、.setting
的配置文件中。
例如 .env
文件:
PORT = 8000
PATH = "home/foo/bar"
MODE = "happy mode"
ZONE = 8
AREA = "Taipei"
你可以手写以下逻辑
- 读取文件
.env
- 通过
\n
分离 - 将数据通过
=
分离出来并存入HashMap
也可以使用一个 crate 帮你实现这些功能
dotenv_codegen
dotenv_codegen 是一个带有宏的 .env
配置解析器。
通过这个 crate 读取 .env
,很容易使用。
fn main() {
println!("{}", dotenv!("PORT"));
}
环境变量
你可能还想调用系统中的环境变量,如环境变量 JAVA_HOME
。
use std::env;
let key = "HOME";
match env::var_os(key) {
Some(val) => println!("{}: {:?}", key, val),
None => println!("{} is not defined in the environment.", key)
}
错误处理
错误处理非常重要。我们不想程序经常 panic!
,也不希望当它遭遇错误后就退出程序。有时候,错误不是很致命,我们需要在不退出程序的情况下处理错误,比如在遇到错误时运行特定的规则或逻辑。
panic
panic!("this is panic");
以下是一些简便但不优雅的处理方式
- 直接退出程序
- 退出时,不指定错误码
- 以脚本的方式使用
Result
Result
在没有崩溃的情况下传递错误。如果函数中断,它将返回 Error
并带有错误类型信息。然后我们根据类型决定下一步做什么,如“重试”,或者“放弃”。
enum MyErr {
Reason1,
Reason2,
}
fn foo() -> Result<(), MyErr> {
match bar {
Some(_)=>{}
Nono => Err(MyErr::Reason1)
}
}
fn hoo() {
match foo() {
Ok(_) => reply(),
Err(e) => println!(e)
// `e` not work yet
// 我们需要 `fmt` 将信息转换一下
}
}
错误消息
你可能要打印或者使用错误消息。此时,你需要为 MyErr
实现(impl
)格式化(fmt
),这样就会拥有自己定义的错误消息。
enum MyErr {
Reason1(String),
Reason2(String, u32),
}
impl fmt::Display for MyErrError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
MyErr::Reason1(ref s) =>
write!(f, "`{}` is the error", s),
MyErr::Reason2(ref s, ref num) =>
write!(f, "`{}` and `{}` are error", s, num),
}
}
}
Err(e) => println!("{}", e)
// `XXX` is the error
标准错误
在操作系统中,有标准输出和标准错误。
println!()
是标准输出,eprintln!()
是标准错误。
例如:
$ cargo run > output.txt
此时标准输出流会重定向到 output.txt
文件中。
那么,如果我们想要把错误消息写到一个日志文件中,可以像“标准错误”一样使用 eprintln!()
来展示错误信息。
退出 code
非 0 值的“退出码”可以让其他程序知道运行的程序有异常。
use std::process;
fn main() {
process::exit(1);
}
结语
CLI 程序完成任意类型的任务,并且好的 CLI 程序需要良好的设计。它可以解析参数和配置。它可以读取环境变量。它还能处理错误。能将信息输出到标准输出和标准错误流中。当它异常退出时会有对应的退出码。
【译】写个好的 CLI 程序的更多相关文章
- Python3的tkinter写一个简单的小程序
一.这个学期开始学习python,但是看了python2和python3,最后还是选择了python3 本着熟悉python的原因,并且也想做一些小程序来增加自己对python的熟练度.所以写了一个简 ...
- (原创)如何使用boost.asio写一个简单的通信程序(一)
boost.asio相信很多人听说过,作为一个跨平台的通信库,它的性能是很出色的,然而它却谈不上好用,里面有很多地方稍不注意就会出错,要正确的用好asio还是需要花一番精力去学习和实践的,本文将通过介 ...
- 用Racket语言写了一个万花筒的程序
用Racket语言写了一个万花筒的程序 来源:https://blog.csdn.net/chinazhangyong/article/details/79362394 https://github. ...
- 如何用 Python 写一个简易的抽奖程序
不知道有多少人是被这个头图骗进来的:) 事情的起因是这样的,上周有同学问小编,看着小编的示例代码敲代码,感觉自己也会写了,如果不看的话,七七八八可能也写的出来,但是一旦自己独立写一段程序,感觉到无从下 ...
- Python写一个自动点餐程序
Python写一个自动点餐程序 为什么要写这个 公司现在用meican作为点餐渠道,每天规定的时间是早7:00-9:40点餐,有时候我经常容易忘记,或者是在地铁/公交上没办法点餐,所以总是没饭吃,只有 ...
- 快速构建CLI程序并发布到PyPi
构造一个简单的CLI程序 typer 这个从去年就被各种营销号吹成Web框架的 第三方库, 与 FastAPI 同出一人之手,它不是Web框架,它是一个用来构建CLI程序的库,我们就简单搞个例子 # ...
- [pixhawk笔记]4-如何写一个简单的应用程序
本文主要内容来自于:https://dev.px4.io/en/tutorials/tutorial_hello_sky.html,并对文档中的部分问题进行更正. 本文假设已经建立好开发环境并能正确编 ...
- hive--构建于hadoop之上、让你像写SQL一样编写MapReduce程序
hive介绍 什么是hive? hive:由Facebook开源用于解决海量结构化日志的数据统计 hive是基于hadoop的一个数据仓库工具,可以将结构化的数据映射为数据库的一张表,并提供类SQL查 ...
- 使用 .NET WinForm 开发所见即所得的 IDE 开发环境,实现不写代码直接生成应用程序
直接切入正题,这是我09年到11年左右业余时间编写的项目,最初的想法很简单,做一个能拖拖拽拽就直接生成应用程序的工具,不用写代码,把能想到的业务操作全部封装起来,通过配置的方式把这些业务操作组织起来运 ...
随机推荐
- django orm介绍以及字段和参数
Object Relational Mapping (ORM) orm介绍 orm概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数 ...
- Mac下安装MySQL8的问题
黑苹果用了一段时间之后,发现很多方面用起来比Windows还舒服些,没什么具体指标,就是纯粹一种感觉. 所以,慢慢将很多程序都迁移过来,在迁移过程中发现的一些有意思的事儿,我都把他们记录下来.如果,不 ...
- 曼孚科技:AI算法领域常用的39个术语(下)
算法是人工智能(AI)核心领域之一. 本文整理了算法领域常用的39个术语,希望可以帮助大家更好地理解这门学科. 本文为下半部分,上半部分见本账号上一篇文章. 19.迁移学习(Transfer Lear ...
- Luogu P1993 题解
p1993 小康的农场 CSP_S 1=之后就没怎么写题解.. 推荐博客食用 预备知识 明显这是一道差分约束的题,以下简称差分 有些人可能不了解差分,请点 [传送门] 至于用差分做的题的特征,无一都是 ...
- 推荐7款用于PHP的代码调试工具,太有用了!
当谈论到服务器端的脚本语言时,大多数人都会首先想到PHP.在你写完代码后,使用一些检查方面的工具是非常有必要的,无论是单纯的代码调试工具,还是测试优化工具.下面就为广大的PHP开发者介绍7个专为PHP ...
- Codeforces667D(spfa+dp)
题意: 给定一个带权有向图,若P(A,B)表示节点A到B的最短路长度,选择四个节点ABCD,使得P(A,B)+P(B,C)+P(C,D)最大. 节点数n在1,000以内,边数m在2,000以内. 思路 ...
- 小白的java学习之路 "类的无参方法"
Java注释: //:单行注释 /**/:多行注释 /** */:JavaDoc注释语法: 访问修饰符 返回值类型 方法名(){ 方法体 } 举例: public void run(){ System ...
- Codeforces Round #616 (Div. 2) B. Array Sharpening
t题目链接:http://codeforces.com/contest/1291/problem/B 思路: 用极端的情况去考虑问题,会变得很简单. 无论是单调递增,单调递减,或者中间高两边低的情况都 ...
- Windows修改账户名称和任务管理器中服务对应的用户名称
新安装的Windows10,在激活的时候如果选择使用微软账户登录,比如我的微软账户名是QQ邮箱,系统激活后,系统盘用户目录(users)下面的用户目录文件夹名称为QQ邮箱的前几位数字,这样其实也在使用 ...
- C语言-浮点数的秘密
一.浮点数的秘密 1.内存中的浮点数 浮点数在内存中的存储方式为:符号位.指数.尾数 十进制浮点数的内存表示: 实例分析: #include <stdio.h> //打印十进制的内存表示 ...