[Go 入门] 第十六章 使用命令行程序
Go 入门系列参考于互联网资料与 人民邮电出版社 《Go 语言入门经典》 与 《Effective Go》,编写目的在于学习交流,如有侵权,请联系删除
命令行程序也叫命令行使用程序或工具, 它被设计到终端运行, 本文内容:
理解命令行
在图形界面(GUI)面世前,与计算机交互通常都是通过命令行进行的。当前,对程序员和系统管理员来说,命令行依然是一种流行且使用的与底层操作系统交互的方式
- 为创建能够定期自动运行的脚本
- 为创建于系统中的文件交互的脚本
- 为创建能够执行系统维护任务的脚本
- 为避免设计图形用户界面这种无谓的开销
操作输入和输出
编写命令行程序前,必须明白一些理论,确保编写的脚本能够与操作系统和其他脚本交互,命令行程序操作输入和输出。对于这些输入和输出,Windows、macOS 和 Linux 使用的术语相同,而 Go 语言也使用这些术语。
名称 | 代码 | 描述 |
---|---|---|
标准输入 | 0 | 包含提供给程序的输入 |
标准输出 | 1 | 包含显示到屏幕上的输出 |
标准错误 | 2 | 包含显示到屏幕上的错误消息 |
标准输入是提供命令行程序的数据,它可以是文件,也可以是文本字符串,
标准输出是来自程序的输出,标准错误是来自程序的错误
长期运行的进程(如 Web 服务器)常常将数据同时记录到标准输入和标准输出,即通常将数据发送到日志文件。与命令行程序相关的生态系统几乎都是从正确地使用标准流(标准输入、标准输出、标准错误)的程序衍生来的。
访问命令行参数
在创建命令行程序方面,Go 语言提供了强大的支持。它遵循接受输入并发送输出的理念,且通常会自动确保输出被发送到正确的输出流。在命令行中传递给命令行程序的数据被称为参数。在Go语言中,要读取传递给命令行程序的参数,可使用标准库的os
包
1 |
|
分析命令行标志
虽然可使用 os
包来获取命令行参数,但Go语言还在标准库中提供了flag
包。flag
提供了众多其他功能,包括以下几点:
- 指定作为参数传递的值的类型
- 设置标志的默认值
- 自动生成帮助文本
1 |
|
解读如下:
- 声明变量s并将其设置为 flag.String 返回的值
- flag.String 能够让您声明命令行标志,并制定其名称、默认值和帮助文本
- 调用 flag.Parse, 让程序能够传递声明的参数
- 最后,打印变量 s 的值,flag.String 返回的是一个指针,因此使用运算符*对其解除引用,以便显示底层的值
如果运行,则 flag
包为 s 设置了默认的值
1 |
|
也可以为使用 -s 给标志指定值
1 |
|
flag
包会自动创建一些帮助文本,要显示它们,可使用如下任何标志:
- -h
- —h
- -help
- —help
1 |
|
指定标志的类型
flag
包根据声明分析标志的类型,这对应于 Go 语言的类型系统。编写命令行程序时,必须考虑程序将接收的数据,并将其映射到正确的类型,这一点很重要
1 |
|
执行这个示例时,可通过传入值来修改标志的值。请注意,对于 Bool 标志,如果没有指定,则默认为 true
其次,flag
包有三种语法格式,请注意:
1 |
|
1 |
|
自定义帮助文本
虽然 flag
包会自动生成帮助文本,但完全可以覆盖默认的帮助格式并提供自定义的帮助文本,为此可将变量 Usage
设置为一个函数
1 |
|
这里使用了标准库中的 os
包来将消息打印到标准误差 (standard Error),因为这条消息将在发现分析错误时显示,但输出是完全可定制的
创建子命令
很多命令行程序都支持子命令,一个典型的示例是 git
,它包含顶级命令 git
和多个子命令,而这些子命令都有独立的选项和帮助文本,下面是 git
的一堆子命令
1 |
|
如果运行这些子命令时指定标志 —help,它们将会有独立的选型。flag
包通过FlagSets
提供了子命令支持,让您能够创建子命令,并指定独立的标志集。要创建子命令并指定标志,可像下面这样做:
cloneCmd := flag.NewFlagSet("clone", flag.ExitOnError)
其中第一个参数为子命令名,而第二个参数指定了错误处理行为
- flag.ContinueOnError: 如果没有分析错误,就继续执行
- flag.ExitOnError: 如果有错误分析,就退出并将状态码,设置为2
- flag.PanicOnError: 如果发生分析错误,就引发 Panic
使用 NewFlagSet 可创建独立的标志集。要根据参数做相应的处理,可使用 switch 语句。os.Args 包含原始参数,因此可在 swtich
语句中使用它来处理标志集。请注意,由于索引从0开始,因此这里需要使用索引1
1 |
|
在下面的示例中,创建了一个命令行工具,它提供了两个命令: uppercase 和 lowercase,这些命令接受标志 -s 或 —s 指定的字符串,并返回处理后的文本。当然,这个实例很简单,这里只是演示如何创建子命令,而不是复杂的逻辑
1 |
|
解读如下:
- 创建了两个
FlagSet
, 一个表示命令uppercase
,另一个表示命令lowercase
- 使用
switch
语句读取命令的第一个参数 - 如果这个参数为
uppercase
,就在FlagSet uppercase
中初始化一个字符串标志,再将其他参数传递给FlagSet uppercase
,并对它们进行分析 - 将 s 的值传递给
strings
包中的方法ToUpper
,以便将其转换为大写。如果用户没有给 s 指定值,将传递默认的空字符串 - 对于
FlagSet lowercase
,逻辑与此相同 - 如果参数既不是
uppercase
也不是lowercase
,执行default
当前,这个程序的顶层什么都没有做,例如,如果用户执行这个程序时,没有提供任何参数,则什么都不会做。
如果想在什么都没有传入的情况下,输出帮助文本:
1 |
|
如果用户没有输入任何参数,则会输出帮助文本 hello world
POSIX 兼容性
在 Linux 和 macOS 中,大多数命令行工具都要求以推荐标准 POSIX 指定的方式传递参数。POSIX 是一系列标准,旨在确保操作系统之间彼此兼容。很多程序员都希望采用这种方式,虽然 flag
包没有遵循这些推荐标准,但有多个第三方替代品遵循了这些推荐标准。
安装和分享命令行程序
开发好命令行程序后,可以在系统中安装它,以便在任何地方,而不只是在命令 go build
生成的二进制文件所在的文件夹中才能访问它,为了遵循 Go 语言的约定,请设置好 $GOPATH ,使用标准的目录布局
新建一个 dirname.go:
1 |
|
随后命令行执行
1 |
|
完成后,打开新的终端,执行
1 |
|
问题列表
如何查看命令退出的状态
要查看命令的退出状态,可在 Windows 系统中使用 echo %errorlevel%,在 Linux 或 macOS 系统中,使用 echo $?
Go 为啥将 -option 和 —option 视为同一选项
虽然二者连字符不是一样的,但 Go 设计者将它们视为同一选项
使用 go install 安装他人提供的命令行安装吗
在安装时,最好检查一下包的内容,在访问操作系统时, Go 程序的权限很大