-
Notifications
You must be signed in to change notification settings - Fork 0
/
第 12.04.02、flag 包
378 lines (233 loc) · 10.8 KB
/
第 12.04.02、flag 包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
总览
flag 包实现了命令行 flag 解析
用法:
使用 flag.String(), Bool(), Int() 等定义 flag
下面的代码定义了一个整型 flag, -flagname,存储在指针 ip 中,其具有类型 *int
import "flag"
var ip = flag.Int("flagname", 1234, "help message for flagname")
如果你愿意,也可以使用 Var() 函数,将 flag 绑定至一个变量上
var flagvar int
func init() {
flag.IntVar(&flagvar, "flagname", 123, "help message for flagname")
}
或者也可以创建自定义的 flag,其满足 Value 接口(具有指针接收者的),并耦合它们至 flag,解析方法
flag.Var(&flagVal, "name", "help message for flagname")
对于这类 flag,默认值就仅为变量的初始值
在定义好了所有的 flag 之后,调用
flag.Parse()
来将命令行解析为定义的 flag
flag 接着就可以直接使用了
若你使用 flag 本身,它们就是指针;若将它们绑定至变量,它们就是值
fmt.Println("ip has value ", *ip)
fmt.Println("flagvar has value ", flagvar)
在解析之后,在 flag 之后跟随的参数就成为了 slice flag.Args() 的内容,或者独立的成为 flag.Arg(i) 的内容
参数的索引从 0 至 flag.NArg()-1
命令行 flag 语法:
-flag
-flag=x
-flag x // 仅用于非布尔 flag
一个或两个减号均可用,它们是等价的
最后一个样式不可用于布尔 flag,因为在命令
cmd -x *
中,若有一个文件叫做 0 或者 false 等,那么命令的含义就改变了
你必须使用 -flag=false 样式来关闭一个布尔 flag
flag 解析在第一个非 flag 参数(“-”是一个非 flag 参数)之前或在终止符 “--” 之后停止解析
整型 flag 接受 1234,0664,0x1234,也可以是负数
布尔 flag 接受:
1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False
Duration flag 接受任何对 time.ParaseDuration 有效的输入
默认的命令行 flag 集受到顶层函数(top-level function)的控制
FlagSet 类型允许邓毅独立的 flag 集,比如实现一个命令行接口的子命令
FlagSet 的方法类似于命令行 flag 集的顶层函数
// 下面这些内容展示了 flag 包的更加复杂的用法
package flag_test
import (
"errors"
"flag"
"fmt"
"strings"
"time"
)
// 例子 1:一个单字符串 flag “species” 具有默认值 “gopher”
var species = flag.String("species", "gopher", "the species we are studying")
// 例子 2:两个 flag 分享了同一个变量,所以我们可以设置简称(shorthand)
// 初始化的顺序是未定义的,所以要保证两者都使用的相同的默认值
// 他们必须使用 init 函数来设置
var gopherType string
func init() {
const (
defaultGopher = "pocket"
usage = "the variety of gopher"
)
flag.StringVar(&gopherType, "gopher_type", defaultGopher, usage)
flag.StringVar(&gopherType, "g", defaultGopher, usage+" (shorthand)")
}
// 例子 3:一个用户定义的 flag 类型,一个 []time.Duration
type interval []time.Duration
// String 是格式化 flag 值的方法,也是 flag.Value 接口的一部分
// String 方法的输出将用于诊断
func (i *interval) String() string {
return fmt.Sprint(*i)
}
// Set 是设置 flag 值的方法,也是 flag.Value 接口的一部分
// Set 的参数是一个将要被解析设置为 flag 值得字符串
// 它是一个逗号分隔的值,所以我们要分离它
func (i *interval) Set(value string) error {
// 若我们希望允许 flag 被设置多次
// 并累积值,我们可以删除这个 if 语句
// 这可以允许用法:
// -deltaT 10s -deltaT 15s
// 或者其它的用法
if len(*i) > 0 {
return errors.New("interval flag already set")
}
for _, dt := range strings.Split(value, ",") {
duration, err := time.ParseDuration(dt)
if err != nil {
return err
}
*i = append(*i, duration)
}
return nil
}
// 定义一个 flag 来累积 duration
// 由于他是特殊类型,我们需要使用 Var 函数,同时在 init 过程中创建 flag
var intervalFlag interval
func init() {
// 将命令行 flag 绑定至 intervalFlag 变量,并设置一个用法信息
flag.Var(&intervalFlag, "deltaT", "comma-separated list of intervals to use between events")
}
func Example() {
// 所有有趣的部分就是上面变量的声明了,但为了保证 flag 包能看到这里定义的 flag,
// 一个东西必须要执行,通常它在 main 函数的起始处执行(并不是 init 函数中!):
// flag.Parse()
// 我们并不会运行它,因为这不是一个 main 函数,而且测试套件已经解析了 flag 了
}
变量
CommandLine 是命令行 flag 的默认集,从 os.Args 解析而来
顶层函数例如 BoolVar Arg 和其他的都是包裹 CommandLine 的方法而来
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
ErrHelp 是当 -help 或者 -h flag 被调用但并没有定义该 flag 时返回的错误
var ErrHelp = errors.New("flag: heko requested")
Usage 将记录了所有定义命令行 flag 的用法(usage)信息打印至 StdErr
当解析 flag 时,遇到了一个错误时被调用
函数时一个变量,可以修改为指向某个自定函数
默认情况下,它打印一个简单的 header,并调用 PrintDefaults;
要了解输出的格式化情况和如何控制它,参看 PrintDefaults 的文档
var Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
PrintDefaults()
}
Arg(i int) string
返回第 i 个命令行参数
Arg(0) 是在 flag 被处理之后,第一个剩余的参数
若,不存在需要的函数,则本函数返回空字符串
Args() []string
返回非 flag 命令行参数
Bool(name string, value bool, usage string) *bool
定义了一个 bool flag,其具有指定的名称,默认值,以及用法(Usage)字符串
返回的值为指向存储了 flag 的值的 bool 值
BoolVar(p *bool, name string, value bool, usage string)
定义了一个 bool flag,其具有指定的名称,默认值,以及用法(Usage)字符串
参数 p 指向一个 bool 变量,其存储了 flag 的值
#! 具有与 Bool 和 BoolVar 类似函数的类型还有:
Duration
Float64
Int
Int64
String
Uint
Uint64
Var(value Value, name string, usage string)
定义了一个 flag,其具有指定的名字和用法字符串
flag 的类型和值被第一个参数,也就是 Value 类型所表示,
Value 这个值通常持有用户定义的 Value 的实现
举例来说,调用者可以创建一个 flag,通过给 slice 定义 Value 的方法,将逗号分隔的字符串转换为 []string;
就这个案例来说,Set 会将逗号分隔的字符串解码至 slice 中
NArg() int
当所有的 flag 都被处理之后,剩余的为处理参数数
NFlag() int
返回已经被设置的命令行 flag 的数量
Parse()
从 os.Args[1:] 中解析命令行 flag
必须在所有的 flag 都定义完成,且任何 flag 被程序使用之前,调用
Parsed() bool
报告命令行 flag 是否已经被解析
PrintDefaults()
任意没有特殊的配置,则本函数将所有定义过的命令行 flag 的默认用法信息输出至 StdErr
对于整型 flag x,默认的输出有以下的形式
-x int
usage-message-for-x (default 7)
对于除了 单字节布尔 flag 名 之外的所有用法信息,都会显示为不同的行
对于布尔 flag,其类型被隐去,且若 flag 名仅有一个字节组成,则用法信息将在同一行显示
若默认值是该类型的零值,则隐去多余的默认值
所罗列的类型,在这里是 int,可以通过用反引号,截取一部分用法信息,来修改;
第一个信息中这样的项目,会作为参数名显示在信息中,而反引号本身将在显示时被移除
举例来说,给定
flag.String("I", "", "search `directory` for include files")
对应的输出为
-I directory
Search directory for include files
Set(name, value string) error
设置名为 name 的命令行 flag 的值
UnquoteUsage(flag *Flag) (name string, usage string)
从给定 flag 的用法字符串中提取一个反引号引起的名称,并返回它以及 un-quote 的用法
给定 "a `name` to show",它返回("name", "a name to show")
若没有反引号,则 name 为对 flag 值的类型的高等猜测,或者,若 flag 为布尔值,则为空字符串
Visit(fn func(*Flag))
以字符顺序浏览命令行 flag,并对每个 flag 调用 fn
它仅会浏览已经被设置的 flag
VisitAll(fn func(*Flag))
以字符顺序浏览命令行 flag,并对每个 flag 调用 fn
它会浏览所有的 flag,即便 flag 未被设置
ErrorHandling int 类型
ErrorHanding 定义了当解析失败时 FlagSet.Parse 应该有的行为
const (
ContinueOnError ErrorHandling = itoa // 返回一个描述性的 error
ExitOnError // 调用 os.Exit(2)
PanicOnError // 调用 panic,并附带一个描述性的 error
)
Flag struct 类型
一个 Flag 代表了一个 flag 的状态
type Flag struct {
Name string // 在命令行显示的名称
Usage string // 帮助信息
Value Value // 设置的值
DefValue string // 默认值(以文本的格式);用于用法信息
}
Lookup(name string) *Flag
返回指定名称的命令行 flag 的 Flag 结构,若不存在则返回 nil
FlagSet struct 类型
一个 FlagSet 表示一个定义了的 flag 的集合
一个 FlagSet 的零值,是没有名称,且错误处理为 ContinueOnError 的值
FlagSet struct {
// 本字段是,在解析 flag 的过程中,当一个错误发生时,所调用的函数
// 该字段时一个函数(而非一个方法),该函数可以被修改为指向一个自定义的错误处理器上
Usage func()
// 包含过滤或者未导出的字段
}
#! 由于本类型的方法是 flag 包顶层函数的超集,所以这里只展示与顶层函数不同的部分
NewFlagSet(name string, errirHandling ErrorHandling) *FlagSet
返回一个新的,空的 flag 集,其具有指定的名字和错误处理属性
(f *FlagSet) Lookup(name string) *Flag
返回指定名称的 flag 的 Flag 结构,若不存在则返回 nil
(f *FlagSet) SetOutput(output io.Writer)
设置用法和错误信息的目的地
若 output 是 nil,os.Stderr 将被使用
Getter interface 类型
Getter 是一个接口,该接口允许 Value 的内容被取回
它包裹了 Value 的接口,而非成为它的一部分,这是由于在 Go 1 之后出现,且需要符合兼容规则
本包提供的所有的 Value 类型都满足 Getter 接口
type Getter interface {
Value
Get() interface{}
}
Value interface 类型
Value 是存储在一个 flag 中的动态值的接口(默认值以字符串表示)
若一个 Value 具有 IsBoolFlag() 方法,且返回为 true,命令行解析者将 -name 等价为 -name=true,而非使用下一个命令行参数
Set 会以命令行的顺序,对每个出现的 flag,调用一次
flag 包可能会对零值接受者调用 String 方法,比如一个 nil 指针
type Value interface {
String() string
Set(string) error
}