我正在构建一个小的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()
}
}
答案 0 :(得分: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 :(得分:1)
这就是标志包处理参数的方式。参数处理总是在某种程度上由约定驱动,并且标志包肯定不遵循许多习惯的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包使用的约定,而不是试图对抗它。