返回介绍

8.2 flag 包

发布于 2024-08-14 12:50:31 字数 10734 浏览 0 评论 0 收藏 0

标签是被传入程序来控制行为的特殊格式化字符串。如果您想支持多个标签,您自己处理标签可能会非常麻烦。因此,如果您开发 Unix 系统命令行工具的话,如果可替代您会发现 flag 包是非常有趣和有用。它的特点中,flag 包不承担命令行参数和选项的排序,并当执行命令行工具错误时它会打印帮助信息。

flag 包最大的优势是它是 Go 的标准库,这意味着它可有广泛的用于测试和调试。

我将介绍两个使用 flag 包的 Go 程序:一个简单的和一个稍复杂的。

第一个是 simpleFlag.go,分为四部分。simpleFlag.go 有两个命令行选项:第一个是布尔选项,第二个需要一个整数值。

simpleFlag.go 的第一部分包含如下代码:

package main

import (
    "flag"
    "fmt"
)

simpleFlag.go 的第二部分如下:

func main(){
    minuk := flag.Bool("k", true, "k")
    miusO := flag.Int("O", 1, "O")
    flag.Parse()

flag.Bool("k", true, "k") 语句定义了一个名为 k 默认值伪 true 的布尔值命令选项。该语句最后的参数是作为程序的使用信息来显示的。同样,flag.Int() 函数增加整数命令选项支持。

您总是要在定义了您想要的命令行选项后调用 flag.Parse()

simpleFlag.go 的第三段代码如下:

valueK := *minusK
valueO := *minusO
valueO++

从上面的代码您可以看到您可以获取到选项值。在这里 flag 包自动把分配给 flag.Int() 标签的输入值转为整数值。这意味着您不必自己做。另外,flag 包确保分配了一个可接受的整数值。

simpleFlag.go 的其余代码如下:

    fmt.Println("-k:", valueK)
    fmt.Println("-O:", valueO)
}

在获得期望的参数后,您现在就可以使用它们了。

执行 simpleFlag.go 产生如下输出:

$go run simpleFlag.og -O 100
-k: true
-O: 101
$go run simpleFlag.og -O=100
-k: true
-O: 101
$go run simpleFlag.og -O=100 -k
-k: true
-O: 101
$go run simpleFlag.og -O=100 -k false
-k: true
-O: 101
$go run simpleFlag.og -O=100 -k=false
-k: true
-O: 101

如果执行 simpleFlag.go 发生错误时,您会看到如下的错误信息:

$go rum simpleFlag.go -O=notAnInterger
invalid value "ontAnInteger" for lag -O: strconv.ParseInt: parsing "notAnInteger": invalid syntax
Usage of /var/folders/sk/ltk8cnw501zdtr2hxcj5sv2m0000gn/T/go-build020625525/command-line arguments/_obj/exe/simpleFlag:
    -O int
        O (default 1)
    -k k (default true)
exit status 2

注意这个方便的使用信息是当您的程序输入了错误的命令行选项时自动打印的。

现在是时候介绍一个更实际和复杂点的使用 flag 包的程序了——funWithFlag.go,分为五部分来介绍。funWithFlag.go 有各种选项,包括一个可以接收用逗号分隔的多值选项。另外,它会说明您如果能够访问位于执行程序末尾并不属于任何选项的命令行参数。

flag.Var() 函数在 funWithFlag.go 中用于创建一个实现了 flag.Value 接口的任意类型的标签,该接口定义如下:

type Value interface {
    String() string
    Set(string) error
}

funWithFlag.go 的第一部分如下:

package main

import(
    "flag"
    "fmt"
    "strings"
)

type NamesFlag struct {
    Names []string
}

NamesFlag 数据结构用于 flag.Value 接口。

funWithFlag.go 的第二部分如下:

func (s *NamesFlag) GetNames() []string{
    return s.Names
}
func (s *NamesFlag) String() string {
    return fmt.Sprint(s.Names)
}

funWithFlag.go 的第三部分代码如下:

func (s *NamesFlag) Set(v string) error {
    if len(s.Names) > 0 {
        return fmt.Errorf("Cannot use names flag more than once!")
    }

    names := strings.Split(v, ",")
    for _, item := range names {
        s.Names = append(s.Names, item)
    }
    return nil
}

首先,Set() 方法确保相关命令行选项没有被设置。之后,获取输入并使用 strings.Split() 函数来分隔参数。最后,参数被保存在 NamesFlag 结构的 Names 字段。

funWithFlag.go 的第四部分代码如下:

func main() {
    var manyNames NamesFlag
    minusK := flag.Int("k:", 0, "An int")
    minusO := flag.String("o", "Mihalis", "The name")
    flag.Var(&manyNames, "names", "Comma-separated list")

    flag.Parse()
    fmt.Println("-k:", *minusK)
    fmt.Println("-o:", *minusO)

funWithFlag.go 的最后部分如下:

    for i, item := range manyNames.GetNames() {
        fmt.Println(i, item)
    }
    fmt.Println("Remaing command-line arugments:")
    for index, val := range flag.Args() {
        fmt.Println(index, ":", val)
    }
}

flag.Args() 切片保留命令行参数,而 manyNames 变量保留来自 flag.Var() 命令行选项的值。

执行 funWithFlag.go 产生如下输出:

""

除非您开发一个不需要选项的命令行小工具,否则您极需要使用 flag 包来处理您的程序的命令行选项。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文