golang标志在第一个非选项后停止解析

问题描述 投票:3回答:2

我正在构建一个小的cli工具,可以在开发或生产中启动我的应用程序。

我希望它的工作方式是这样的:

app run --dev or app run --prod

Atm它不会在我的命令之后解析标志,而是在我的命令之前解析。这样可行

app --dev run or app --prod run

任何想法如何解决这个,所以我可以在我的命令后使用它?这是我的代码

func main() {
    //flag.Usage := usage
    flag.Parse()
    args := flag.Args()
    if len(args) == 0 {
        Usage()
        os.Exit(0)
    }

    if *dev {
        os.Setenv("ENV", "development")
    }

    if *prod {
        os.Setenv("ENV", "production")
    }

    switch {
    // Run
    case args[0] == "run" && len(args) == 1:
        os.Setenv("port", *port)
        log.Printf("Booting in %s", os.Getenv("ENV"))
        Run()

    // Help
    case args[0] == "help" && len(args) == 1:
        Usage()
    }
}
go flags command-line-interface
2个回答
4
投票

传统上,UNIX选项解析器getopt()在第一个非选项后停止解析。 glibc改变了这种行为以支持任意位置的选项,这是一个有争议的决定。 flag包实现了传统行为。

您可以做的一件事是在解析标志之前置换arguments数组。这就是glibc的作用:

func permutateArgs(args []string) int {
    args = args[1:]
    optind := 0

    for i := range args {
        if args[i][0] == '-' {
            tmp := args[i]
            args[i] = args[optind]
            args[optind] = tmp
            optind++
        }
    }

    return optind + 1
}

此代码排列args,使选项位于前面,使程序名称保持不变。 permutateArgs返回排列后第一个非选项的索引。使用这样的代码:

optind := permutateArgs(os.Args)
flags.Parse()

// process non-options
for i := range os.Args[optind:] {
    // ...
}

1
投票

这只是flag包处理参数的方式。参数处理总是在某种程度上由约定驱动,并且标志包肯定不遵循许多习惯的gnu样式。

一种方法是在命令之前对选项进行排序:

// example argument sorting using sort.Stable
type ArgSlice []string

func (p ArgSlice) Len() int      { return len(p) }
func (p ArgSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

func (p ArgSlice) Less(i, j int) bool {
    if len(p[i]) == 0 {
        return false
    }
    if len(p[j]) == 0 {
        return true
    }
    return p[i][0] == '-' && p[j][0] != '-'
}

func main() {

    args := []string{"cmd", "-a", "arg", "-b", "-c"}
    sort.Stable(ArgSlice(args))
    // if sorting os.Args, make sure to omit the first argument
    // sort.Stable(ArgSlice(os.Args[1:]))

    fmt.Println(args)
}

许多软件包对子命令使用单独的FlagSet,这提供了很好的分离,但是当子命令之间可能存在不同的标志时,这不起作用。唯一的解决方案是在每个级别复制标记。

但是,最好的答案仍然是遵循flag包使用的约定,而不是试图对抗它。

© www.soinside.com 2019 - 2024. All rights reserved.