Golang学习笔记--flag包

原文链接:http://www.cloudnoter.com/?p=131

flag包是用来处理命令参数的。总得来说,其通过将命令行标志与某一具体变量进行绑定,开发人员通过使用该变量进行业务逻辑处理。

一、FlagSet是该包的核心类型:

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.
    Usage func()
    name          string
    parsed        bool
    actual        map[string]*Flag    // 存放命令行中实际输入的标志映射
    formal        map[string]*Flag  // 存放该实例可以处理的命令行标志映射
    args          []string // arguments after flags 存放非标志的参数列表(即标志后面的参数)
    exitOnError  bool    // does the program exit if there's an error?
    errorHandling ErrorHandling
    output        io.Writer // nil means stderr; use out() accessor
}

该类型同时提供了一系列的方法集合【MethodSet】,通过该方法集用于可以实现灵活的命令行标志处理。

二、flag包export的变量:CommandLine

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

该包提供了一个默认变量:CommandLine,其为FlatSet的一个变量(用面向对象的术语叫做:FlagSet的一个实例)

该flag包export的所有函数本质上都是调用FlagSet类型变量(实例):CommandLine的方法实现。如下:

// Int defines an int flag with specified name, default value, and usage string.
// The return value is the address of an int variable that stores the value of the flag.
func Int(name string, value int, usage string) *int {
    return CommandLine.Int(name, value, usage)
}

三、flag包支持的标志格式

Command line flag syntax:
        -flag // 代表bool值,相当于-flag=true
        -flag=x
        -flag x  // non-boolean flags only  不支持bool值标志
    One or two minus signs may be used; they are equivalent.  // -flag=value与--flag=value是等效的
    The last form is not permitted for boolean flags because the
    meaning of the command
        cmd -x *
    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 "--".  // 碰到连续两个"-"号且参数长度为2时则终止标志解析

四、标志绑定相关方法:以绑定int类型为例。

// IntVar defines an int flag with specified name, default value, and usage string.
// The argument p points to an int variable in which to store the value of the flag.
// FlagSet提供的绑定int类型标志的方法,无返回值。通过传入int类型指针变量进行绑定,当调用该方法后,会将绑定信息存入FlagSet.formal映射中
func (f *FlagSet) IntVar(p *int, name string, value int, usage string) {
    f.Var(newIntValue(value, p), name, usage)
}
//与上述方法相对应的flag包export的函数:
// IntVar defines an int flag with specified name, default value, and usage string.
// The argument p points to an int variable in which to store the value of the flag.
func IntVar(p *int, name string, value int, usage string) {
    CommandLine.Var(newIntValue(value, p), name, usage)
}
//使用示例
var flagvar int
flag.IntVar(&flagvar, "flagname2", 1234, "help message for flagname2")

// Int defines an int flag with specified name, default value, and usage string.
// The return value is the address of an int variable that stores the value of the flag.
// FlagSet提供的绑定int类型标志的方法,有返回值,返回int类型指针,当调用该方法后,会将绑定信息存入FlagSet.formal映射中
func (f *FlagSet) Int(name string, value int, usage string) *int {
    p := new(int)
    f.IntVar(p, name, value, usage)
    return p
}
//与上述方法相对应的flag包export的函数:
// Int defines an int flag with specified name, default value, and usage string.
// The return value is the address of an int variable that stores the value of the flag.
func Int(name string, value int, usage string) *int {
    return CommandLine.Int(name, value, usage)
}
//使用示例
var flagvar = flag.Int("flagname", 1234, "help message for flagname")

五、解析标志的相关关键源码

// Parse parses the command-line flags from os.Args[1:].  Must be called
// after all flags are defined and before flags are accessed by the program.
// flag包export的函数,调用时机为:在设置好标志与变量的绑定关系后,调用flag.Parse()。
func Parse() {
    // Ignore errors; CommandLine is set for ExitOnError.
    CommandLine.Parse(os.Args[1:])
}

// Parse parses flag definitions from the argument list, which should not
// include the command name.  Must be called after all flags in the FlagSet
// are defined and before flags are accessed by the program.
// The return value will be ErrHelp if -help was set but not defined.
// FlagSet类型提供的实现方法
func (f *FlagSet) Parse(arguments []string) error {
    f.parsed = true
    f.args = arguments
    for {
        seen, err := f.parseOne()
        if seen {
            continue
        }
        if err == nil {
            break
        }
        switch f.errorHandling {
        case ContinueOnError:
            return err
        case ExitOnError:
            os.Exit(2)
        case PanicOnError:
            panic(err)
        }
    }
    return nil
}

// parseOne parses one flag. It reports whether a flag was seen.
// 解析每个标志并返回相关结果,若碰到 '-' 或 '--' 时也会直接终止整个标志解析过程,每解析成功一个标志就会将该标志信息放入FlagSet.actual映射中
func (f *FlagSet) parseOne() (bool, error) {
    if len(f.args) == 0 {
        return false, nil
    }
    s := f.args[0]
    if len(s) == 0 || s[0] != '-' || len(s) == 1 {
        return false, nil
    }
    num_minuses := 1
    if s[1] == '-' {
        num_minuses++
        if len(s) == 2 { // "--" terminates the flags
            f.args = f.args[1:]
            return false, nil
        }
    }
    name := s[num_minuses:]
    if len(name) == 0 || name[0] == '-' || name[0] == '=' {
        return false, f.failf("bad flag syntax: %s", s)
    }

    // it's a flag. does it have an argument?
    f.args = f.args[1:]
    has_value := false
    value := ""
    for i := 1; i < len(name); i++ { // equals cannot be first
        if name[i] == '=' {
            value = name[i+1:]
            has_value = true
            name = name[0:i]
            break
        }
    }
    m := f.formal
    flag, alreadythere := m[name] // BUG
    if !alreadythere {
        if name == "help" || name == "h" { // special case for nice help message.
            f.usage()
            return false, ErrHelp
        }
        return false, f.failf("flag provided but not defined: -%s", name)
    }
    if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg
        if has_value {
            if err := fv.Set(value); err != nil {
                return false, f.failf("invalid boolean value %q for  -%s: %v", value, name, err)
            }
        } else {
            fv.Set("true")
        }
    } else {
        // It must have a value, which might be the next argument.
        if !has_value && len(f.args) > 0 {
            // value is the next arg
            has_value = true
            value, f.args = f.args[0], f.args[1:]
        }
        if !has_value {
            return false, f.failf("flag needs an argument: -%s", name)
        }
        if err := flag.Value.Set(value); err != nil {
            return false, f.failf("invalid value %q for flag -%s: %v", value, name, err)
        }
    }
    if f.actual == nil {
        f.actual = make(map[string]*Flag)
    }
    f.actual[name] = flag
    return true, nil
}

时间: 2024-10-10 10:50:37

Golang学习笔记--flag包的相关文章

Golang学习笔记--log包

个人站:http://www.cloudnoter.com/?p=137 一.快速使用 Golang的log包短小精悍,可以非常轻松的实现日志打印转存功能.不用多说,log支持并发操作(即协程安全-相对于JAVA中的线程安全而言),其结构定义如下: type Logger struct { mu sync.Mutex // ensures atomic writes; protects the following fields prefix string // prefix to write a

golang学习笔记————字符串

字符串的创建 在golang中 字符串是使用双引号("")包裹住的字符序列: 字符是使用单引号('')包裹住的单一字符: 声明字符串的方式:var strvalue string 声明并创建的方式:strvalue := "This is a string!"; KeyPoint: 一旦字符串变量被初始化后,则不可单独改变该字符串序列中的某一字符:但该字符串变量可以重新被赋值: Ex:   strvalue := "abcd" fmt.Print

Android学习笔记--design包下的两个控件

今天学习了design包下的两个控件,记录一下,首先需要我们依赖 1 compile 'com.android.support:design:25.0.0' 之后在XML文件中就可以使用了 1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 x

golang学习笔记:golang 语法篇(二)

在语法篇(一)中学习了go中基本的数据类型.变量.常量等组成语言的基本要素,在这一节中将会学习如何将这些元素组织起来,最终写成可以执行的代码. 在这一部分包括: go中的流程控制语句: go中函数的用法: go特殊的错误处理方式: Golang中的流程控制语句 在具体编程的时候免不了需要使用一些特殊的语句实现某些功能,比如使用循环语句来进行迭代,使用选择语句控制程序的执行方式等.这些语句在任何一门程序设计语言 中都会有支持,golang中除了支持常用的循环,条件选择语句以外,还支持跳转语句,下面

互联网世界中的C语言——我的golang学习笔记:1(基础语法快速过)

前言 学习任何知识都会有一个学习背景 最近,我们团队乃至我司整个云服务,上go的呼声越来越高!新服务已经开始用go开发,部分现有Java版的服务重构为go也只是时间问题而已,故相关技术积累势在必行!在云网络的分布式服务乃至在一切高并发,分布式后台服务中,golang都有着很大的优势. 据我对国内互联网行业的实际考察,了解,目前国内主流互联网公司都在积极投入go的怀抱…… 青云更是全栈使用了go…… 还有火的一塌糊涂的docker. 它为云而生. 它为并发而生. 还有go的安全.简洁.高效 有良好

Golang学习笔记(1)---go程序一般结构

Go程序是通过 package来组织的(与python的库类似) 只有package名称为main的包可以包涵main函数(同时main函数也是一个程序的入口) 一个可执行程序有且仅有一个main包 通过import关键字来引入其他非main包 通过const关键字来进行常量的定义 通过在函数体外部使用var关键字来进行全局变量的声明与赋值 通过type关键字来进行结构(struct)或接口(interface)的声明----一般(自定义)类型 通过func关键字来进行函数的声明 一般格式为:

golang学习笔记(1):安装&amp;helloworld

安装: golang编译器安装过程比较简单,也比较快,不同平台下(win/linux/macos)都比较相似: https://dl.gocn.io/golang/1.9.2/go1.9.2.src.tar.gz  下载对应的系统版本的编译器 go的版本号由"." 分为3部分 如当前的最新版本为1.9.2,那么其中第一个数字代表go的大版本,目前为1.0版本: 第二个数字表示小版本,主要是各种优化与BUG修复,以及一些新的语言特性: 第三个数字表示紧急修复版本: golang官方承诺大

Linux学习笔记——程序包管理之rpm命令

RPM rpm是Linux上一个很好用的程序包管理管理器,它具有安装.卸载.升级.查询.校验.数据库维护等功能. 下面分别介绍一下rpm的各个功能: 安装: rpm {-i|--install} [install-options] PACKAGE_FILE ... -v:详细显示安装过程 -vv:相对-v更加详细 -h: 以#显示程序包管理执行进度:每个#表示2%的进度 安装过程如图 另外还有一些其他选项 [install-options] --test: 测试安装,但不真正执行安装过程:dry

Linux学习笔记——程序包管理之yum

YUM yum 是rpm程序包管理器的前段管理器.yum 主要功能是更方便的添加/删除/更新RPM 包,自动解决包的倚赖性问题,便于管理大量系统的更新问题. yum 的操作是基于yum 仓库进行的 yum repository: yum repo  yum仓库 存储了众多rpm包,以及包的相关的元数据文件(放置于特定目录下:repodata): 文件服务器: ftp:// http:// nfs:// file:/// yum 的配置 配置文件 /etc/yum.conf:为所有仓库提供公共配置