Golang标志:忽略缺失的标志并解析多个重复的标志

huangapple go评论71阅读模式
英文:

Golang flag: Ignore missing flag and parse multiple duplicate flags

问题

我是你的中文翻译助手,以下是翻译好的内容:

我是Go语言的新手,我一直无法找到使用flag解决这个问题的方法。

我该如何使用flag,使得我的程序可以处理像下面这样的调用,其中-term标志可能出现多次,包括0次:

./myprogram -f flag1
./myprogram -f flag1 -term t1 -term t2 -term  t3
英文:

I am new to Golang and I have been unable to find a solution to this problem using flag.

How can I use flag so my program can handle calls like these, where the -term flag may be present a variable number of times, including 0 times:

./myprogram -f flag1
./myprogram -f flag1 -term t1 -term t2 -term  t3

答案1

得分: 14

你需要声明自己的类型,该类型实现了Value接口。以下是一个示例。

// 为了接受多个输入而创建
type arrayFlags []string

func (i *arrayFlags) String() string {
    // 更改此处,这只是一个示例,满足接口要求
    return "我的字符串表示"
}

func (i *arrayFlags) Set(value string) error {
    *i = append(*i, strings.TrimSpace(value))
    return nil
}

然后在解析标志的主函数中:

var myFlags arrayFlags

flag.Var(&myFlags, "term", "我的术语")
flag.Parse()

现在,所有的术语都包含在切片myFlags中。

英文:

You need to declare your own type which implements the Value interface. Here is an example.

// Created so that multiple inputs can be accecpted
type arrayFlags []string

func (i *arrayFlags) String() string {
    // change this, this is just can example to satisfy the interface
	return "my string representation"
}

func (i *arrayFlags) Set(value string) error {
	*i = append(*i, strings.TrimSpace(value))
	return nil
}

then in the main function where you are parsing the flags

var myFlags arrayFlags

flag.Var(&myFlags, "term", "my terms")
flag.Parse()

Now all the terms are contained in the slice myFlags

答案2

得分: 0

这个问题很有趣,可以有很多变化。

  • 数组(Array)
  • 映射(Map)
  • 结构体(Struct)

核心内容与@reticentroot的回答相同。

以下是尽可能提供相关链接的示例:

示例

预期用法:

type Books []string

func (*Books) String() string   { return "" }
func (*Books) Set(string) error { return nil }

type Dict map[string]string

func (*Dict) String() string   { return "" }
func (*Dict) Set(string) error { return nil }

type Person struct {
	Name string
	Age  int
}

func (*Person) String() string   { return "" }
func (*Person) Set(string) error { return nil }

func pseudocode() {
	flagSetTest := flag.NewFlagSet("test", flag.ContinueOnError)

	books := Books{}
	flagSetTest.Var(&books, "book", "-book C++ -book Go -book javascript")
	// 预期输出:books: []string{C++,Go,javascript}

	dict := Dict{}
	flagSetTest.Var(&dict, "dict", "-dict A:65|B:66")
	// 预期输出:dict: map[string]string{"A":"65", "B":"66"}

	// 映射
	person := Person{}
	flagSetTest.Var(&person, "person", "-person Name:foo|Age:18")
	// 输出:{Name:foo Age:18}

	flagSetTest.Parse(os.Args[1:])
	fmt.Println(person, books, dict)
}

完整代码

package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"os"
	"reflect"
	"strconv"
	"strings"
)

type BooksValue []string

// https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L298
func (arr *BooksValue) String() string {
	/*
		value.String(): https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L870
		DefValue string:
			- https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L348
			- https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L914-L920
			- https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L529-L536
			- https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L464
	*/
	return ""
}

// https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L299
func (arr *BooksValue) Set(value string) error {
	/*
		value: https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L947
		bool: Set(value): https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L966-L975
		else: Set(value): https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L986-L988
	*/
	*arr = append(*arr, strings.TrimSpace(value))
	return nil
}

type DictValue map[string]string

func (m *DictValue) String() string {
	return ""
}

func (m *DictValue) Set(value string) error {
	arr := strings.Split(value, "|") // "key1:val1|key2:val2|..."
	for _, curPairStr := range arr {
		itemArr := strings.Split(curPairStr, ":")
		key := itemArr[0]
		val := itemArr[1]
		(*m)[key] = val
	}
	return nil
}

type PersonValue struct {
	Name     string
	Age      int
	Msg      string
	IsActive bool
}

func (s *PersonValue) String() string {
	return ""
}

func (s *PersonValue) Set(value string) error {
	arr := strings.Split(value, "|") // "Field1:Value1|F2:V2|...|FN:VN"

	for _, curPairStr := range arr {
		itemArr := strings.Split(curPairStr, ":")
		key := itemArr[0]
		val := itemArr[1]

		// [Access struct property by name](https://stackoverflow.com/a/66470232/9935654)
		pointToStruct := reflect.ValueOf(s)
		curStruct := pointToStruct.Elem()
		curField := curStruct.FieldByName(key)
		if !curField.IsValid() {
			return errors.New("not found")
		}

		// CanSet one of conditions: Name starts with a capital
		if !curField.CanSet() {
			return errors.New("can't set")
		}

		t := reflect.TypeOf(*s)
		structFieldXXX, isFound := t.FieldByName(key)
		if !isFound {
			return errors.New("not found")
		}

		switch structFieldXXX.Type.Name() {
		case "int":
			// https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L146-L153
			intValue, err := strconv.ParseInt(val, 0, strconv.IntSize)
			if err != nil {
				return errors.New("parse error: [int]")
			}
			curField.SetInt(intValue)
		case "bool":
			// https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L117-L121
			boolValue, err := strconv.ParseBool(val)
			if err != nil {
				return errors.New("parse error: [bool]")
			}
			curField.SetBool(boolValue)
		case "string":
			curField.SetString(val)
		default:
			return errors.New("not support type=" + structFieldXXX.Type.Name())
		}
	}

	return nil
}

func main() {

	flagSetTest := flag.NewFlagSet("test", flag.ContinueOnError)

	// 数组
	books := BooksValue{}
	flagSetTest.Var(&books, "book", "-book Go -book javascript ...")

	// 映射
	myMap := DictValue{}
	flagSetTest.Var(&myMap, "map", "-dict A:65|B:66")

	// 结构体
	person := PersonValue{Msg: "Hello world"}
	flagSetTest.Var(&person, "person", "-person Name:string|Age:int|Msg:string|IsActive:bool")

	testArgs := []string{"test",
		"-book", "Go", "-book", "javascript", // testArray
		"-map", "A:65|B:66|Name:Carson", // testMap
		"-person", "Name:Carson|Age:30|IsActive:true", // testStruct
	}

	testFunc := func(args []string, reset bool) {
		if reset {
			books = BooksValue{}
			myMap = DictValue{}
			person = PersonValue{}
		}

		if err := flagSetTest.Parse(args); err != nil {
			fmt.Printf(err.Error())
		}
		fmt.Printf("%+v\n", books)
		fmt.Printf("%+v\n", myMap)
		fmt.Printf("%+v\n", person)
	}

	testFunc(testArgs[1:], false)

	// ↓ 自己玩耍
	scanner := bufio.NewScanner(os.Stdin)
	for {
		fmt.Println("Enter CMD: ") // 例如:test -book item1 -book item2 -map key1:value1|key2:v2 -person Age:18|Name:Neil|IsActive:true
		scanner.Scan()             // 从标准输入中扫描一行
		text := scanner.Text()     // 获取扫描到的字符串
		args := strings.Split(text, " ")

		switch args[0] {
		case "quit":
			return
		case "test":
			testFunc(args[1:], true)
		}
	}
}

<kbd>go playground</kbd>

英文:

This question is an interesting one and can play in many variations.

  • Array
  • Map
  • Struct

The core content is the same as @reticentroot answered,

> Complete the definition of this interface: Flag.Value

The following are examples to share and provide relevant links as much as possible

Example

expected usage:

type Books []string

func (*Books) String() string   { return &quot;&quot; }
func (*Books) Set(string) error { return nil }

type Dict map[string]string

func (*Dict) String() string   { return &quot;&quot; }
func (*Dict) Set(string) error { return nil }

type Person struct {
	Name string
	Age  int
}

func (*Person) String() string   { return &quot;&quot; }
func (*Person) Set(string) error { return nil }

func pseudocode() {
	flagSetTest := flag.NewFlagSet(&quot;test&quot;, flag.ContinueOnError)

	books := Books{}
	flagSetTest.Var(&amp;books, &quot;book&quot;, &quot;-book C++ -book Go -book javascript&quot;)
	// expected output: books: []string{C++,Go,javascript}

	dict := Dict{}
	flagSetTest.Var(&amp;dict, &quot;dict&quot;, &quot;-dict A:65|B:66&quot;)
	// expected output: dict: map[string]string{&quot;A&quot;:&quot;65&quot;, &quot;B&quot;:&quot;66&quot;}

	// map
	person := Person{}
	flagSetTest.Var(&amp;person, &quot;person&quot;, &quot;-person Name:foo|Age:18&quot;)
	// output: {Name:foo Age:18}

	flagSetTest.Parse(os.Args[1:])
	fmt.Println(person, books, dict)
}

Full code

package main

import (
	&quot;bufio&quot;
	&quot;errors&quot;
	&quot;flag&quot;
	&quot;fmt&quot;
	&quot;os&quot;
	&quot;reflect&quot;
	&quot;strconv&quot;
	&quot;strings&quot;
)

type BooksValue []string

// https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L298
func (arr *BooksValue) String() string {
	/*
		value.String(): https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L870
		DefValue string:
			- https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L348
			- https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L914-L920
			- https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L529-L536
			- https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L464
	*/
	return &quot;&quot;
}

// https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L299
func (arr *BooksValue) Set(value string) error {
	/*
		value: https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L947
		bool: Set(value): https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L966-L975
		else: Set(value): https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L986-L988
	*/
	*arr = append(*arr, strings.TrimSpace(value))
	return nil
}

type DictValue map[string]string

func (m *DictValue) String() string {
	return &quot;&quot;
}

func (m *DictValue) Set(value string) error {
	arr := strings.Split(value, &quot;|&quot;) // &quot;key1:val1|key2:val2|...&quot;
	for _, curPairStr := range arr {
		itemArr := strings.Split(curPairStr, &quot;:&quot;)
		key := itemArr[0]
		val := itemArr[1]
		(*m)[key] = val
	}
	return nil
}

type PersonValue struct {
	Name     string
	Age      int
	Msg      string
	IsActive bool
}

func (s *PersonValue) String() string {
	return &quot;&quot;
}

func (s *PersonValue) Set(value string) error {
	arr := strings.Split(value, &quot;|&quot;) // &quot;Field1:Value1|F2:V2|...|FN:VN&quot;

	for _, curPairStr := range arr {
		itemArr := strings.Split(curPairStr, &quot;:&quot;)
		key := itemArr[0]
		val := itemArr[1]

		// [Access struct property by name](https://stackoverflow.com/a/66470232/9935654)
		pointToStruct := reflect.ValueOf(s)
		curStruct := pointToStruct.Elem()
		curField := curStruct.FieldByName(key)
		if !curField.IsValid() {
			return errors.New(&quot;not found&quot;)
		}

		// CanSet one of conditions: Name starts with a capital
		if !curField.CanSet() {
			return errors.New(&quot;can&#39;t set&quot;)
		}

		t := reflect.TypeOf(*s)
		structFieldXXX, isFound := t.FieldByName(key)
		if !isFound {
			return errors.New(&quot;not found&quot;)
		}

		switch structFieldXXX.Type.Name() {
		case &quot;int&quot;:
			// https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L146-L153
			intValue, err := strconv.ParseInt(val, 0, strconv.IntSize)
			if err != nil {
				return errors.New(&quot;parse error: [int]&quot;)
			}
			curField.SetInt(intValue)
		case &quot;bool&quot;:
			// https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L117-L121
			boolValue, err := strconv.ParseBool(val)
			if err != nil {
				return errors.New(&quot;parse error: [bool]&quot;)
			}
			curField.SetBool(boolValue)
		case &quot;string&quot;:
			curField.SetString(val)
		default:
			return errors.New(&quot;not support type=&quot; + structFieldXXX.Type.Name())
		}
	}

	return nil
}

func main() {

	flagSetTest := flag.NewFlagSet(&quot;test&quot;, flag.ContinueOnError)

	// array
	books := BooksValue{}
	flagSetTest.Var(&amp;books, &quot;book&quot;, &quot;-book Go -book javascript ...&quot;)

	// map
	myMap := DictValue{}
	flagSetTest.Var(&amp;myMap, &quot;map&quot;, &quot;-dict A:65|B:66&quot;)

	// struct
	person := PersonValue{Msg: &quot;Hello world&quot;}
	flagSetTest.Var(&amp;person, &quot;person&quot;, &quot;-person Name:string|Age:int|Msg:string|IsActive:bool&quot;)

	testArgs := []string{&quot;test&quot;,
		&quot;-book&quot;, &quot;Go&quot;, &quot;-book&quot;, &quot;javascript&quot;, // testArray
		&quot;-map&quot;, &quot;A:65|B:66|Name:Carson&quot;, // testMap
		&quot;-person&quot;, &quot;Name:Carson|Age:30|IsActive:true&quot;, // testStruct
	}

	testFunc := func(args []string, reset bool) {
		if reset {
			books = BooksValue{}
			myMap = DictValue{}
			person = PersonValue{}
		}

		if err := flagSetTest.Parse(args); err != nil {
			fmt.Printf(err.Error())
		}
		fmt.Printf(&quot;%+v\n&quot;, books)
		fmt.Printf(&quot;%+v\n&quot;, myMap)
		fmt.Printf(&quot;%+v\n&quot;, person)
	}

	testFunc(testArgs[1:], false)
	
	// ↓ play by yourself
	scanner := bufio.NewScanner(os.Stdin)
	for {
		fmt.Println(&quot;Enter CMD: &quot;) // example: test -book item1 -book item2 -map key1:value1|key2:v2 -person Age:18|Name:Neil|IsActive:true
		scanner.Scan()             // Scans a line from Stdin(Console)
		text := scanner.Text()     // Holds the string that scanned
		args := strings.Split(text, &quot; &quot;)

		switch args[0] {
		case &quot;quit&quot;:
			return
		case &quot;test&quot;:
			testFunc(args[1:], true)
		}
	}
}

<kbd>go playground</kbd>

huangapple
  • 本文由 发表于 2017年8月3日 22:38:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/45487377.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定