Part2

GO语言的flag包:

flag的定义:
flag.String(), Bool(), Int() //这里这是列举了几个
然后就是两种定义的方式:

var ip = flag.Int("flagname", 1234, "help message for flagname") //ip 为指针类型,Int或者
String返回的都是指针类型

flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")//此处需要在外面手动定义flagvar

第一种方式是返回一个值,第二种方式是把变量的传进去。

通过flag.Var()绑定自定义类型,自定义类型需要实现Value接口(Receives必须为指针),如:

flag.Var(&flagVal, "name", "help message for flagname")

对于这种类型的flag,默认值为该变量类型的初始值

解析命令行参数

Go语言标准库中的flag包专门用于接收和解析命令参数。

flag的解析
flag.Parse()
解析函数将会在碰到第一个非flag命令行参数时停止,非flag命令行参数是指不满足命令行语法的参数,如命令行参数为cmd --flag=true abc则第一个非flag命令行参数为“abc”

flag解析后的参数使用
fmt.Println("ip has value ", *ip)
fmt.Println("flagvar has value ", flagvar)
命令行语法
-flag //只支持bool类型
-flag=x
-flag x //只支持非bool类型
注意第三种只支持非bool类型,原因就是如果文件的名字为false的话那么就会产生歧义。看例子:

package main

import (
    "flag"
    "fmt"
)

var name string
var right bool
func init() {
    flag.StringVar(&name, "name", "everyone", "The greeting object.")
    flag.BoolVar(&right, "check", false, "The right ob")
}

func main() {
    flag.Parse()
    fmt.Println(name)
    fmt.Println(right)
}

这里采用的还是老师的例子,只多加了个bool类型。
我们编译之后生成exe

demo4.exe -check false
demo4.exe -check

对于这两种都会采用默认的bool类型true。

demo4.exe -check=false

只有这样的时候,才会采用false

-name 无论有无=都会显示传入的字符串。

eg2:

package main

import (
	"flag"
	"fmt"
)

var name string
var name1 string

func init() {
	flag.StringVar(&name, "name", "Nobody", "请设置名字")
	name1 = *flag.String("name1", "Nobody", "请设置名字")
}

func main() {
	flag.Parse()
	fmt.Printf("Hello, %s!\n", name)
	fmt.Printf("Hello, %s!\n", name1)
}

函数flag.StringVar接受4个参数:

  • 第1个参数,是用于存储该命令参数的值的地址,就是示例中声明的变量name的地址,由表达式&name表示
  • 第2个参数,是为了指定该命令参数的名称,这里是name
  • 第3个参数,是为了指定在未追加该命令参数时的默认值,这里是Nobody
  • 第4个参数,是该命令参数的简短说明,这在打印命令说明时会用到

还有一个函数flag.String和flag.StringVar类似。就是没有第一个参数了,而是直接返回一个已经分配好的用于存储命令参数值的地址。

函数调用顺序

函数flag.Parse用于真正解析命令参数,并把它们的值赋给相应的变量。
该函数的调用必须在所有命令参数的声明和设置之后,并且在读取任何命令参数值之前。
也就是像例子里做的,把参数设置的语句放在init函数里先执行,然后把flag.Parse放在main函数的开头,在调用使用命令行参数的语句之前。

自定制参数使用说明

在上面的例子中,执行的时候带上-h或者–help参数,就能看到如下的使用说明:

PS G:\Steed\Documents\Go\src\Go36\article02\example01> go run main.go --help
Usage of C:\Users\Steed\AppData\Local\Temp\go-build897600374\command-line-arguments\_obj\exe\main.exe:
  -name string
        请设置名字 (default "Nobody")
exit status 2
PS G:\Steed\Documents\Go\src\Go36\article02\example01>

输出的第一行在Usage of后面一长串的路径,是go run命令构建上述命令源码文件时临时生成的可执行文件的完整路径。如果是编译之后再执行,就是可执行文件的相对路径,就没那么难看了。
并且这一行的说明内容是可以自定制的,接下来就是通过包提供的方法对这行说明进行自定制。这3种方法是一层一层更加接近底层的调用的。

通过flag.Usage定制

最简单的一种定制方式就是对变量flag.Usage重新赋值。为该变量定义一个函数,在要打印说明的时候,其实就是调用执行了这个方法:

package main

import (
	"flag"
	"fmt"
	"os"
)

var name string

func init() {
    // 下面2句语句的顺序随意
	flag.StringVar(&name, "name", "Nobody", "请设置名字")
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s\n", "Say Hello")
		flag.PrintDefaults()
	}
}

func main() {
	flag.Parse()
	fmt.Printf("Hello, %s!\n", name)
}

flag.Usage的赋值必须在调用flag.Parse()之前。

通过flag.CommandLine定制

在调用flag包中的一些函数(比如StringVar、Parse等等)的时候,实际上是在调用flag.CommandLine变量的对应方法。
flag.CommandLine相当于默认情况下的命令参数容器。通过对flag.CommandLine重新赋值,就可以更深层次地定制当前命令源码文件的参数使用说明。
仅修改之前的init函数部分,去掉flag.Usage的赋值语句,改为通过对flag.CommandLine赋值来进行定制:

func init() {
    // flag.CommandLine对象的创建必须在前面执行
	flag.CommandLine = flag.NewFlagSet("", flag.ExitOnError)
	//flag.CommandLine = flag.NewFlagSet("", flag.PanicOnError)
	flag.CommandLine.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s\n", "Say Hello")
		flag.PrintDefaults()
	}
	flag.StringVar(&name, "name", "Nobody", "请设置名字")
}

这样修改后的效果和之前完全一致。这里主要出分离出了下面这句:

flag.CommandLine = flag.NewFlagSet("", flag.ExitOnError)

这句的第一个参数应该是没有用的了,这里传入空字符串。
第二个参数可以是一下的3个常量:

const (
	ContinueOnError ErrorHandling = iota // Return a descriptive error.
	ExitOnError                          // Call os.Exit(2).
	PanicOnError                         // Call panic with a descriptive error.
)

定义在解析遇到问题后,是执行何种操作。默认的就是ExitOnError,所以在–help执行打印说明后,最后一行会出现“exit status 2”,以状态码2退出。
这里可以根据需要定制为抛出Panic。

创建私有命令参数容器

这里的代码上上面的例子差不多,依然是调用flag.NewFlagSet()创建命令参数容器。不过这次把容器赋值给自定义的变量:

package main

import (
	"flag"
	"fmt"
	"os"
)

var name string
var cmdLine = flag.NewFlagSet("cmdLine Say Hello", flag.ExitOnError)

func init() {
	cmdLine.StringVar(&name, "name", "Nobody", "请设置名字")
}

func main() {
	cmdLine.Parse(os.Args[1:])
	fmt.Printf("Hello, %s!\n", name)
}

首先通过命令 var cmdLine = flag.NewFlagSet("cmdLine Say Hello", flag.ExitOnError)创建了私有的命令参数容器。
然后,之后其他所有方法的调用都通过这个变量来调用的。
上面这样做之后,就完全脱离了flag.CommandLine。而是使用 *flag.FlagSet 类型的变量 cmdLine 进行各种调用了。该类型拥有很多方法,可以继续探索。
主要是可以更灵活地定制命令参数容器。并且你的定制完全不会影响到全局变量flag.CommandLine。

思考题

1. 默认情况下,我们可以让命令源码文件接受哪些类型的参数值?
这个从flag包的代码包里面就可以看到,这里贴一下班里一个同学的答案

    int(int|int64|uint|uint64),
    float(float|float64)
    string,
    bool,
    duration(时间),
    var(自定义)


2. 我们可以把自定义的数据类型作为参数值的类型吗?如果可以,怎样做?

    当然是可以的:
    // 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 Var(value Value, name string, usage string) {
        CommandLine.Var(value, name, usage)
    }

    type pepoInfo struct{
        name string
        age int
        homeAdress string
    }

    func (p * pepoInfo) Set(val string) error{
        peopleList := strings.Split(val, ",")
        p.name = peopleList[0]
        p.age,_ =  strconv.Atoi(peopleList[1])
        p.homeAdress = peopleList[2]
        return nil
    }   

    func (s *pepoInfo) String() string {
        infoList := strings.Split("wenxuwan,20,山东", ",")
        s.name = infoList[0]
        s.age,_ = strconv.Atoi(infoList[1])
        s.homeAdress = infoList[2]
        return "It's none of my business"
    }
    func main(){

    var people pepoInfo
    flag.Var(&people, "info", "hao ma fan")
    flag.Parse()
    //打印结果slice接收到的值
    fmt.Println(people)
    }

demo4.exe --info wenxuwan,10,shouguang
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值