目的
命令行界面(Command-line Interfaces)是比较常用的一种软件形式。对于大部分开发运维人员来说很多时候CLIs可能比图形界面更加方便。软件开发时也经常会有需要开发命令行界面形式软件的情况,使用Golang来开发是一种比较不错的选择。这篇文章将简单介绍使用Golang标准库中os.Args和flag包编写命令行界面。
os.Args
标准库 os
包中的 var Args []string
保存了命令行参数,第一个参数( os.Args[0]
)是程序名。
Args hold the command-line arguments, starting with the program name.
能拿到命令行参数就算是编写命令行界面的第一步了。下面的 flag
包可以用来解析这些参数,其内部通过 os.Args[1:]
方式获取参数。
flag
实际使用时命令行参数通常是有多种形态的(比如 app -arg1 --arg2 -arg3=val -arg4 val
),并且顺序也是可以自由调整的,标准库中的 flag
https://pkg.go.dev/flag 包可以用于解析处理命令行参数,方便进一步的使用。
flag
包可以用来处理 -flag value
或者 --flag value
形式的参数选项。(好像必须是这种形式的,单纯的 -flag
或者 --flag
这种形式的不支持)
可以使用 func Type(name string, value Type, usage string) *Type
方法添加要选项, name
是选项名称, value
是默认值, usage
是选项的文本说明, Type
常见的Golang的基础的数据类型都支持。该方法返回一个选项类型数据的指针。
添加了选项后可以使用 func Parse()
进行命令行参数解析。下面是最基础的使用演示:
使用时选项可以是 -flag
--flag
-flag=value
--flag=value
-flag value
--flag value
,布尔类型的选项必须使用 =
赋值。
选项数值类型值可以是 1234
0664
0x1234
-1
-255
等形式的;布尔类型 1
t
T
true
TRUE
True
等都会识别为真, 0
f
F
false
FALSE
False
都会识别为假。
通常使用时同一个选项可以有长短两种表达,使用 flag
包可以使用 func TypeVar(p *Type, name string, value Type, usage string)
方法来处理。该方法和前面的很像,只不过之前通过方法创建返回的变量现在需要自己创建后传入。下面是使用演示:
flag
包会自动添加帮助选项 -h
--h
-help
--help
:
这个帮助选项输出的信息也可以自定义:
对于命令行参数中剩余的部分可以使用下面一些方法来获取个数和内容等:
func NArg() int
func Arg(i int) string
func Args() []string
到目前为止虽然只是在将命令行参数获取和解析等内容,但这就是编写命令行界面程序最核心的部分了,剩下的无法是根据解析得到的内容进行相应的处理。
FlagSet
flag
包还可以使用其中的 FlagSet
来进行更加精细化的操作,比如设置子命令等。下面是个简单的演示:
package main
import (
"flag"
"fmt"
"os"
)
func fncmda() {
cmda := flag.NewFlagSet("cmda", flag.ContinueOnError) // 创建子命令解析器
sFlag := cmda.String("s", "naisu", "help msg for cmda -s") // 设置子命令选项
err := cmda.Parse(os.Args[2:]) // 从下标2的参数开始解析
if err != nil {
fmt.Println(err)
}
fmt.Println(*sFlag)
}
func fncmdb() {
cmdb := flag.NewFlagSet("cmdb", flag.ContinueOnError) // 创建子命令解析器
iFlag := cmdb.Int("i", 233, "help msg for cmdb -i") // 设置子命令选项
err := cmdb.Parse(os.Args[2:]) // 从下标2的参数开始解析
if err != nil {
fmt.Println(err)
}
fmt.Println(*iFlag)
}
func main() {
if len(os.Args) < 2 {
os.Args = append(os.Args, "default")
}
switch os.Args[1] { // 根据不同子命令进行不同处理
case "cmda":
fncmda()
case "cmdb":
fncmdb()
default:
fmt.Println("子命令不存在")
}
}
使用这些方式可以实现更加个性化或更加复杂的设置,但是通常不推荐怎么做,如果有更多的需求,更加推荐使用线程的框架来处理。
总结
对于简单的项目使用上面方式开发使用还是挺方便的。对于复杂的项目或是功能更加完善的项目来说使用现有的框架来开发会更加方便。Golang中用来开发命令行界面的框架比较热门的有下面两个: