Go 提供了解析命令行参数的 flag 包,本文旨在介绍 flag 的使用及内部实现等。

1. flag 包使用及实现

type PropertyOfPod struct {
Namespace *string
PodName *string
Phase *string
} var pod = PropertyOfPod{} func init() {
// String defines a string flag with specified name, default value, and usage string.
// The return value is the address of a string variable that stores the value of the flag. pod.Namespace = flag.String("namespace", "default", "resource field for pod")
pod.PodName = flag.String("name", "", "pod name") pod.Phase = new(string) // StringVar defines a string flag with specified name, default value, and usage string.
// The argument p points to a string variable in which to store the value of the flag. flag.StringVar(pod.Phase, "phase", "running", "pod phase")
} func main() {
flag.Parse() fmt.Printf("pod property:\nnamespace: %v, pod name: %v, phase: %v\n", *pod.Namespace, *pod.PodName, *pod.Phase)
}

调用 flag 包的 String 函数定义 flag。示例中,通过 String 和 StringVar 两种方式定义 flag。

具体定义 flag 的 String 函数做了什么呢?接着往下看 String 函数:

func String(name string, value string, usage string) *string {
return CommandLine.String(name, value, usage)
}

String 通过实例化类 CommandLine 调用 String 方法。其中 CommandLine 的类结构体定义为:

var CommandLine = NewFlagSet(os.Args[0], ExitOnError)

type FlagSet struct {
Usage func() name string // 每个 FlagSet 有唯一的 name
parsed bool // parsed 标志记录 FlagSet 是否解析命令行参数
actual map[string]*Flag // 最终记录的实际可用标志
formal map[string]*Flag // 标志信息,未“过滤”
args []string // arguments after flags // 命令行传入参数 os.Args[1:] 写入到 args 中
errorHandling ErrorHandling
output io.Writer // nil means stderr; use Output() accessor
}

FlagSet 的 String 方法又做了什么呢?接着往下看:

func (f *FlagSet) String(name string, value string, usage string) *string {
p := new(string)
f.StringVar(p, name, value, usage)
return p
} func (f *FlagSet) StringVar(p *string, name string, value string, usage string) {
f.Var(newStringValue(value, p), name, usage)
}

在 String 方法内创建了临时指针变量 p, p 的值被传入到 StringVar 函数。这和示例直接调用 StringVar 是一样的,在 newStringValue 函数中,p 将指向 value 的地址:

// -- string Value
type stringValue string func newStringValue(val string, p *string) *stringValue {
*p = val
return (*stringValue)(p)
}

最后调用 Var 方法实现 flag 的定义,Var 方法的第一个参数值得一说,它接受的是接口类型 Value 的值,为什么接受接口类型是因为这里需要多态实现不仅定义 String flag,也能定义 Int,Bool 等类型的 flag。

Var 方法将外部传入参数定义到 Flag 结构体中,并作为值赋给 formal:

func (f *FlagSet) Var(value Value, name string, usage string) {
flag := &Flag{name, usage, value, value.String()}
_, alreadythere := f.formal[name]
if alreadythere {
var msg string
if f.name == "" {
msg = fmt.Sprintf("flag redefined: %s", name)
} else {
msg = fmt.Sprintf("%s flag redefined: %s", f.name, name)
}
fmt.Fprintln(f.Output(), msg)
panic(msg) // Happens only if flags are declared with identical names
}
if f.formal == nil {
f.formal = make(map[string]*Flag)
}
f.formal[name] = flag
}

定义了 flag 之后,还需要 parse 对传入的 flag 进行解析,实例中调用 flag 的 Parse 函数实现解析:

func Parse() {
// Ignore errors; CommandLine is set for ExitOnError.
CommandLine.Parse(os.Args[1:])
} func (f *FlagSet) Parse(arguments []string) error {
f.parsed = true // parse 时将 parsed 标志记为 true
f.args = arguments
for {
seen, err := f.parseOne()
if seen {
continue
}
if err == nil {
break
}
...
}
return nil
}

其中,parseOne 方法解析 args 参数中的 flag,并将解析的 flag 赋给 map actual。

2. flag 方法

2.1 Visit

// Visit visits the command-line flags in lexicographical order, calling fn
// for each. It visits only those flags that have been set. flag.Visit(func(f *flag.Flag) {
key := strings.ToUpper(strings.ReplaceAll(f.Name, "-", "_"))
Flag_visit[key] = string(f.Value.String())
})
fmt.Printf("Flag_visit: %v\n", Flag_visit)

2.2 VisitAll

// VisitAll visits the command-line flags in lexicographical order, calling
// fn for each. It visits all flags, even those not set. flag.VisitAll(func(f *flag.Flag) {
key := strings.ToUpper(strings.ReplaceAll(f.Name, "-", "_"))
Flag_visit[key] = string(f.Value.String())
})
fmt.Printf("Flag_visit: %v\n", Flag_visit)

小白学标准库之 flag的更多相关文章

  1. 小白学 Python 爬虫(21):解析库 Beautiful Soup(上)

    小白学 Python 爬虫(21):解析库 Beautiful Soup(上) 人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前 ...

  2. 小白学 Python 爬虫(22):解析库 Beautiful Soup(下)

    人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...

  3. 小白学 Python 爬虫(23):解析库 pyquery 入门

    人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...

  4. 小白学 Python 爬虫(32):异步请求库 AIOHTTP 基础入门

    人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...

  5. 什么是 C 和 C ++ 标准库?学编程的你应该知道这些知识!

    简要介绍编写C/C ++应用程序的领域,标准库的作用以及它是如何在各种操作系统中实现的. 我已经接触C++一段时间了,一开始就让我感到疑惑的是其内部结构:我所使用的内核函数和类从何而来? 谁发明了它们 ...

  6. 【循序渐进学Python】11.常用标准库

    安装完Python之后,我们也同时获得了强大的Python标准库,通过使用这些标准库可以为我们节省大量的时间.这里是一些常用标准库的简单说明.更多的标准库的说明,可以参考Python文档 sys 模块 ...

  7. 小白学 Python 爬虫(18):Requests 进阶操作

    人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...

  8. 小白学 Python 爬虫(25):爬取股票信息

    人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...

  9. 小白学Java:I/O流

    目录 小白学Java:I/O流 基本分类 发展史 文件字符流 输出的基本结构 流中的异常处理 异常处理新方式 读取的基本结构 运用输入与输出 文件字节流 缓冲流 字符缓冲流 装饰设计模式 转换流(适配 ...

  10. 小白学 Python 数据分析(4):Pandas (三)数据结构 DataFrame

    在家为国家做贡献太无聊,不如跟我一起学点 Python 人生苦短,我用 Python 前文传送门: 小白学 Python 数据分析(1):数据分析基础 小白学 Python 数据分析(2):Panda ...

随机推荐

  1. [Clickhouse] Clickhouse 报SQLException : Read timed out

    1 问题描述 在使用Clickhouse(21.3.4.25)进行大数据量地数据查询,高频报出 SQLException : Read timed out 错误 2 问题分析 2.1 单次查询:耗时约 ...

  2. 封装Detours用于Python中x64函数hook

    Detours 代码仓库: https://github.com/microsoft/Detours x64写一个任意地址hook要比x86麻烦的多,所以这里直接封装框架来用于x64的hook. De ...

  3. 反向 Debug 了解一下?揭秘 Java DEBUG 的基本原理

    Debug 的时候,都遇到过手速太快,直接跳过了自己想调试的方法.代码的时候吧-- 一旦跳过,可能就得重新执行一遍,准备数据.重新启动可能几分钟就过去了. 好在IDE 们都很强大,还给你后悔的机会,可 ...

  4. 【Datahub系列教程】Datahub入门必学——DatahubCLI之Docker命令详解

    大家好,我是独孤风,今天的元数据管理平台Datahub的系列教程,我们来聊一下Datahub CLI.也就是Datahub的客户端. 我们在安装和使用Datahub 的过程中遇到了很多问题. 如何安装 ...

  5. 支付宝 v3 验签如何实现

    上次给大家介绍了 支付宝 v3 自签名如何实现,这次顺便再把验签也写一下. 为什么要验签 说起为什么要验签,如果要详细一点解释的话,可以写很多很多...... 我们就简单一点来解释:验签可以证明接收到 ...

  6. 斐波那契数Fibonacci

    509. 斐波那契数 斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列.该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和.也就是: F(0) = 0,   F(1) = ...

  7. DWS轻量化更新黑科技:宽表加工优化

    本文分享自华为云社区<GaussDB(DWS)性能调优:宽表加工优化方案>,作者:譡里个檔 . 1. 业务背景 宽表加工性能慢,在Gauss(DWS)中可以使用DWS的轻量化更新的黑科技实 ...

  8. 可以一学的代码优化小技巧:减少if-else冗余

    摘要:if-else 语句对于程序员来说,是非常非常熟悉的一个判断语句,我们在日常开发和学习中都经常看见它. 本文分享自华为云社区<JavaScript代码之美-代码优化,减少if-else冗余 ...

  9. 云图说|云上应用监控神器——应用性能监控APM2.0

    阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要: 应用性能管理服务 ...

  10. 一键自动修改和翻新OC源码,解决苹果审核4.3和马甲问题

    ​ ipaguard 自动修改/翻新/混淆/OC/iOS代码,自动替换类名,方法名 由来 网上有很多关于如何混淆iOS源码的方法,但是都不够智能,生成的方法类名要么千奇百怪,要么aaaabbbxxx这 ...