Go之Flag包源码阅读

本文介绍了Go语言中的Flag包源码阅读,特别是关注了StringVar方法的实现。作者通过阅读源码揭示了Flag包如何存储和处理命令行参数,解释了CommandLine结构体的作用,并引导读者理解其他相关方法的工作原理。
摘要由CSDN通过智能技术生成

Go之Flag包源码阅读

  从第一次接触go就对flag包非常懵,因为只看到直接通过包名去调用flag.String(name,value,usage) 装入数据和flag.Parse() 之后取出数据,想不到居然有这样的内置包, 到底把数据存到了哪里呢?
  终于在好奇心驱使下我去读了它的源码,打开后一大串使用说明,真的是非常nice,立刻get到如何实战.

Package flag implements command-line flag parsing.

Usage:

Define flags using flag.String(), Bool(), Int(), etc.

This declares an integer flag, -flagname, stored in the pointer ip, with type *int.
    import "flag"
    var ip = flag.Int("flagname", 1234, "help message for flagname")
If you like, you can bind the flag to a variable using the Var() functions.
    var flagvar int
    func init() {
        flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
    }
Or you can create custom flags that satisfy the Value interface (with
pointer receivers) and couple them to flag parsing by
    flag.Var(&flagVal, "name", "help message for flagname")
For such flags, the default value is just the initial value of the variable.

After all flags are defined, call
    flag.Parse()
to parse the command line into the defined flags.

Flags may then be used directly. If you're using the flags themselves,
they are all pointers; if you bind to variables, they're values.
    fmt.Println("ip has value ", *ip)
    fmt.Println("flagvar has value ", flagvar)

After parsing, the arguments following the flags are available as the
slice flag.Args() or individually as flag.Arg(i).
The arguments are indexed from 0 through flag.NArg()-1.

Command line flag syntax:
    -flag
    -flag=x
    -flag x  // non-boolean flags only
One or two minus signs may be used; they are equivalent.
The last form is not permitted for boolean flags because the
meaning of the command
    cmd -x *
where * is a Unix shell wildcard, will change if there is a file
called 0, false, etc. You must use the -flag=false form to turn
off a boolean flag.

Flag parsing stops just before the first non-flag argument
("-" is a non-flag argument) or after the terminator "--".

Integer flags accept 1234, 0664, 0x1234 and may be negative.
Boolean flags may be:
    1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False
Duration flags accept any input valid for time.ParseDuration.

The default set of command-line flags is controlled by
top-level functions.  The FlagSet type allows one to define
independent sets of flags, such as to implement subcommands
in a command-line interface. The methods of FlagSet are
analogous to the top-level functions for the command-line
flag set.

  要读一个类,我认为先看看定义的结构体,然后去看我们常用的方法内部是如何实现的,因为常用方法一般是一个入口,由此进入更有条理,如果从上至下一行行看容易失去重点。
结构

StringVar方法

func StringVar(p *string, name string, value string, usage string) {
    CommandLine.Var(newStringValue(value, p), name, usage)
}

  这应该是我们非常常用的方法了,深入去看看CommandLine

// CommandLine is the default set of command-line flags, parsed from os.Args.
// The top-level functions such as BoolVar, Arg, and so on are wrappers for the
// methods of CommandLine.
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)

看下NewFlagSet的实现

// NewFlagSet returns a new, empty flag set with the specified name and
// error handling property.
func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
    f := &FlagSet{
        name:          name,
        errorHandling: errorHandling,
    }
    f.Usage = f.defaultUsage
    return f
}

看看defaultUsage是什么吧

// defaultUsage is the default function to print a usage message.
func (f *FlagSet) defaultUsage() {
    if f.name == "" {
        fmt.Fprintf(f.Output(), "Usage:\n")
    } else {
        fmt.Fprintf(f.Output(), "Usage of %s:\n", f.name)
    }
    f.PrintDefaults()
}

这是一组格式转换,深入PrintDefaults方法

// PrintDefaults prints, to standard error unless configured otherwise, the
// default values of all defined command-line flags in the set. See the
// documentation for the global function PrintDefaults for more information.
func (f *FlagSet) PrintDefaults() {
    f.VisitAll(func(flag *Flag) {
        s := fmt.Sprintf("  -%s", flag.Name) // Two spaces before -; see next two comments.
        name, usage := UnquoteUsage(flag)
        if len(name) > 0 {
            s += " " + name
        }
        // Boolean flags of one ASCII letter are so common we
        // treat them specially, putting their usage on the same line.
        if len(s) <= 4 { // space, space, '-', 'x'.
            s += "\t"
        } else {
            // Four spaces before the tab triggers good alignment
            // for both 4- and 8-space tab stops.
            s += "\n    \t"
        }
        s += strings.Replace(usage, "\n", "\n    \t", -1)

        if !isZeroValue(flag, flag.DefValue) {
            if _, ok := flag.Value.(*stringValue); ok {
                // put quotes on the value
                s += fmt.Sprintf(" (default %q)", flag.DefValue)
            } else {
                s += fmt.Sprintf(" (default %v)", flag.DefValue)
            }
        }
        fmt.Fprint(f.Output(), s, "\n")
    })
}

至此我们已经弄清楚了commandLine是什么,我也明白了最初的疑问,为什么这个包直接通过包名装入参数,取出参数,原来都存在了它自己定义的这个CommandLine里面了。
回到我们主方法StringVar(),我们看看
CommandLine的var方法

// Var defines a flag with the specified name and usage string. The type and
// value of the flag are represented by the first argument, of type Value, which
// typically holds a user-defined implementation of Value. For instance, the
// caller could create a flag that turns a comma-separated string into a slice
// of strings by giving the slice the methods of Value; in particular, Set would
// decompose the comma-separated string into the slice.
func (f *FlagSet) Var(value Value, name string, usage string) {
    // Remember the default value as a string; it won't change.
    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包核心方法StringVar的实现,剩下的还有IntVar(), BoolVar()...这些方法都与此类似,各位可以自己去看。而另一个核心方法flag.Parse()的实现就不写了,留给大家自己去解读吧,应该是非常简单的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值