Golang - Flag 包

概述

  • 官方文档地址
  • 接收命令行传参
  • 给出友好的提示信息
  • 推荐这样传参: --name=yky --age=20
  • 主要分为 flagflagSet, flag 使用 flag 底层实现是 flagSet , flagSet 的默认命名是脚本名

方法清单

flag 和 flagSet 共有

  • 定义
    • 数字相关: flag.Int(), flag.Int64(), flag.IntVar(), flag.Int64Var(), flag.Uint(), flag.Uint64(), flag.UintVar(), flag.Uint64Var(), flag.Float64() , flag.Float64Var()
    • 布尔值相关: flag.Bool(), flag.BoolVar()
    • 字符串相关: flag.String(), flag.StringVar()
    • 时长相关: flag.Duration(), flag.DurationVar()
  • 功能
    • flag.Parse(): 解析命令行参数(使用方法略有不同, flagSet 调用需要传入参数切片)
    • flag.Lookup(): 查找指定命名的 flag 变量, 存在返回*flag.Flag, 不存在返回nil
    • flag.PrintDefaults(): 输出帮助信息
    • flag.Var(): 定义
    • flag.Set(): 修改
    • flag.NFlag(): 被命令行设置的 flag 参数的数量
    • 非 flag 变量相关
      • flag.NArg(): 非 flag 参数个数
      • flag.Args(): 非 flag 参数列表
      • flag.Arg(): 根据索引取非 flag 参数内容
  • 判断
    • flag.Parsed(): 判断是否执行过 Parse()

flagSet 独有

  • fs.NewFlagSet(): 初始化一个 flagSet
  • fs.Init():
  • fs.SetOutput: 设置输出的位置, 参数类型为 io.Writer

使用

flag 示例

// 实际 flag 的底层实现是依据 flagSet, flagSetName 是脚本名
package main

import (
	"flag"
	"fmt"
)

func main() {

	// 1. 先定义一个对应类型的变量, 将指针作为参数传给 Var 方法
	// StringVar(接收指针, 参数名, 默认值, 参数说明)
	var name string
	flag.StringVar(&name, "name", "yky", "姓名")

	// 2. 直接注册一个flag变量(返回的是指针)
	// Int(参数名, 默认值, 参数说明)
	age := flag.Int("age", 18, "年龄")

	// 解析命令行参数
	flag.Parse()

	// 注意输出时的不同, 一个是直接输出, 一个是指针转值
	fmt.Printf("name: %v, age: %v\n", name, *age)
}

flagSet 示例

package main

import (
	"flag"
	"fmt"
)

func main() {

    // 先注册一个 flagSet
    fs := flag.NewFlagSet("main", flag.ExitOnError)

    // 使用方法同上, 只是由 flagSet 调用
    var name string
    fs.StringVar(&name, "name", "yky", "姓名")

    age := fs.Int("age", 18, "年龄")

    // 这里不同, 需要传入参数范围, 也就是传参时指定--name和--age的索引
    // 示例中[1:]表示解析所有
    fs.Parse(os.Args[1:])

    fmt.Printf("name: %v, age: %v\n", name, *age)
}

进阶示例

时长示例

package main

import (
	"flag"
	"fmt"
	"time"
)

func main() {

	// 时长示例: 可以接收参数 s(秒), m(分), h(小时)
	sleep := flag.Duration("sleep", 1*time.Second, "延迟执行时间")

	// 解析命令行参数
	flag.Parse()

	// 打印信息
	fmt.Printf("sleeping for %v...\n", *sleep)

	// 执行
	time.Sleep(*sleep)
}

lookup (配合类型断言)

package main

import (
	"flag"
	"fmt"
)

func main() {

	flag.Int("age", 18, "年龄")

	flag.Parse()

	// lookup 返回一个 *flag.Flag
	if flag.Lookup("age") != nil {

		// 此时 v 是一个 Value
		// Value 是一个 Interface, 包括两个方法
		// - String() string
		// - Set(string) error
		v := flag.Lookup("age").Value

		// 可以通过 String(), 将结果完全转换为 string
		fmt.Printf("%T: %[1]v\n", v.String())

		// 也可以通过 (flag.Getter).(type) 转为对应的类型
		// interface.(flag.Getter) 找到外层的接口
		// .Get() 调用接口实现的方法
		// .(type) 配合类型断言
		y := v.(flag.Getter).Get()

		// 已知类型的写法
		z1 := y.(int)
		fmt.Printf("%T: %[1]v\n", z1)

		// 类型断言的标准写法
		z2, ok := y.(int)
		if ok {
			fmt.Printf("%T: %[1]v\n", z2)
		}
	}
}

其他

// Parsed(), 返回 bool, 验证是否执行了 Parse()
flag.Parsed() 

// 设置 flag 值, 内部会将参数转换为对应类型
flag.Lookup("age").Value.Set("190")

// 获取 flag 值
flag.Lookup("age").Value.String()

// NFlag(), 输出被解析的参数个数
fmt.Printf("是否解析命令行参数: %v, 解析匹配个数: %v\n", flag.Parsed(), flag.NFlag())

// NArg: 非 flag 指定的参数个数
if flag.NArg() == 0 {
    fmt.Println("没有额外参数")
else {
    // Args: 非 flag 指定的参数列表
    fmt.Printf("额外参数列表: %v\n", flag.Args())

    // Arg: 根据索引取非 flag 参数值
    fmt.Printf("取索引为 0 的参数作为示例: %v\n", flag.Arg(0))
}

// Usage() 就是输出这个信息(--help)
flag.PrintDefaults()

源码

var

// 使用 flag.xxx 注册变量时, 会通过 CommandLine 调用 NewFlagSet 先定义 flagSet
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)

// 用于打印帮助信息的方法, os.Args[0]即脚本名, 也是默认的 flagSet 的默认名W
var Usage = func() {
	fmt.Fprintf(CommandLine.Output(), "Usage of %s:\n", os.Args[0])
	PrintDefaults()
}

struct

// A Flag represents the state of a flag.
type Flag struct {
	Name     string // name as it appears on command line
	Usage    string // help message
	Value    Value  // value as set
	DefValue string // default value (as text); for usage message
}

// Flag names must be unique within a FlagSet. An attempt to define a flag whose
// name is already in use will cause a panic.
type FlagSet struct {
	// Usage is the function called when an error occurs while parsing flags.
	// The field is a function (not a method) that may be changed to point to
	// a custom error handler. What happens after Usage is called depends
	// on the ErrorHandling setting; for the command line, this defaults
	// to ExitOnError, which exits the program after calling Usage.
	Usage func()

	name          string
	parsed        bool
	actual        map[string]*Flag
	formal        map[string]*Flag
	args          []string // arguments after flags
	errorHandling ErrorHandling
	output        io.Writer // nil means stderr; use Output() accessor
}

Interface

// 实现 String 和 Set 方法的就是 Value
// String 可以使内容类型变为 string
type Value interface {
    String() string
    Set(string) error
}

// 比 Value 多实现一个 Get 方法
type Getter interface {
    Value
    Get() interface{}
}

func

// 用 float64 举例
// -- float64 Value
type float64Value float64

func newFloat64Value(val float64, p *float64) *float64Value {
	*p = val
	return (*float64Value)(p)
}

func (f *float64Value) Set(s string) error {
	v, err := strconv.ParseFloat(s, 64)
	if err != nil {
		err = numError(err)
	}
	*f = float64Value(v)
	return err
}

func (f *float64Value) Get() interface{} { return float64(*f) }

func (f *float64Value) String() string { return strconv.FormatFloat(float64(*f), 'g', -1, 64) }

你可能感兴趣的:(#,Go,golang)