clop 新版本来了, 让你快速从 flag 包迁移过来, 一行命令的事

2021-06-02 10:27:06 +08:00
 guonaihong

clop

一款小巧, 追求开发效果的工具命令行工具包

地址

https://github.com/guonaihong/clop

本次特性

加入代码生成的思路, 可以快速帮你从 flag 包迁移至 clop

1.安装 clop 命令

go get github.com/guonaihong/clop/cmd/clop

2.使用 clop 解析包含 flag 包的代码

就可以把 main.go 里面的 flag 库转成 clop 包的调用方式

clop -f main.go

main.go代码如下

package main

import "flag"

func main() {
	s := flag.String("string", "", "string usage")
	i := flag.Int("int", "", "int usage")
	flag.Parse()
}

输出代码如下

package main

import (
	"github.com/guonaihong/clop"
)

type flagAutoGen struct {
	Flag string `clop:"--string" usage:"string usage" `
	Flag int    `clop:"--int" usage:"int usage" `
}

func main() {
	var flagVar flagAutoGen
	clop.Bind(&flagVar)
}

完整文档如下

clop

clop 是一款基于 struct 的命令行解析器,麻雀虽小,五脏俱全。(从零实现)

feature

内容

Installation

go get github.com/guonaihong/clop

Quick start

package main

import (
	"fmt"
	"github.com/guonaihong/clop"
)

type Hello struct {
	File string `clop:"-f; --file" usage:"file"`
}

func main() {

	h := Hello{}
	clop.Bind(&h)
	fmt.Printf("%#v\n", h)
}
// ./one -f test
// main.Hello{File:"test"}
// ./one --file test
// main.Hello{File:"test"}

example

base type

int

package main

import (
        "fmt"

        "github.com/guonaihong/clop"
)

type IntDemo struct {
        Int int `clop:"short;long" usage:"int"`
}

func main() {
        id := &IntDemo{}
        clop.Bind(id)
        fmt.Printf("id = %v\n", id)
}
//  ./int -i 3
// id = &{3}
// ./int --int 3
// id = &{3}

float64

package main

import (
        "fmt"

        "github.com/guonaihong/clop"
)

type Float64Demo struct {
        Float64 float64 `clop:"short;long" usage:"float64"`
}

func main() {
        fd := &Float64Demo{}
        clop.Bind(fd)
        fmt.Printf("fd = %v\n", fd)
}
// ./float64 -f 3.14
// fd = &{3.14}
// ./float64 --float64 3.14
// fd = &{3.14}

duration

package main

import (
        "fmt"
        "time"

        "github.com/guonaihong/clop"
)

type DurationDemo struct {
        Duration time.Duration `clop:"short;long" usage:"duration"`
}

func main() {
        dd := &DurationDemo{}
        clop.Bind(dd)
        fmt.Printf("dd = %v\n", dd)
}
// ./duration -d 1h
// dd = &{1h0m0s}
// ./duration --duration 1h
// dd = &{1h0m0s}

string

package main

import (
        "fmt"

        "github.com/guonaihong/clop"
)

type StringDemo struct {
        String string `clop:"short;long" usage:"string"`
}

func main() {
        s := &StringDemo{}
        clop.Bind(s)
        fmt.Printf("s = %v\n", s)
}
// ./string --string hello
// s = &{hello}
// ./string -s hello
// s = &{hello}

array

similar to curl command

package main

import (
        "fmt"

        "github.com/guonaihong/clop"
)

type ArrayDemo struct {
        Header []string `clop:"-H;long" usage:"header"`
}

func main() {
        h := &ArrayDemo{}
        clop.Bind(h)
        fmt.Printf("h = %v\n", h)
}
// ./array -H session:sid --header token:my
// h = &{[session:sid token:my]}

similar to join command

加上 greedy 属性,就支持数组贪婪写法。类似 join 命令。

package main

import (
    "fmt"

    "github.com/guonaihong/clop"
)

type test struct {
    A []int `clop:"-a;greedy" usage:"test array"`
    B int   `clop:"-b" usage:"test int"`
}

func main() {
    a := &test{}
    clop.Bind(a)
    fmt.Printf("%#v\n", a)
}

/*
运行
./use_array -a 12 34 56 78 -b 100
输出
&main.test{A:[]int{12, 34, 56, 78}, B:100}
*/

required flag

package main

import (
	"github.com/guonaihong/clop"
)

type curl struct {
	Url string `clop:"-u; --url" usage:"url" valid:"required"`
}

func main() {

	c := curl{}
	clop.Bind(&c)
}

// ./required 
// error: -u; --url must have a value!
// For more information try --help

set default value

可以使用 default tag 设置默认值,普通类型直接写,复合类型用 json 表示

package main

import (
    "fmt"
    "github.com/guonaihong/clop"
)

type defaultExample struct {
    Int          int       `default:"1"`
    Float64      float64   `default:"3.64"`
    Float32      float32   `default:"3.32"`
    SliceString  []string  `default:"[\"one\", \"two\"]"`
    SliceInt     []int     `default:"[1,2,3,4,5]"`
    SliceFloat64 []float64 `default:"[1.1,2.2,3.3,4.4,5.5]"`
}

func main() {
    de := defaultExample{}
    clop.Bind(&de)
    fmt.Printf("%v\n", de) 
}
// run
//         ./use_def
// output:
//         {1 3.64 3.32 [one two] [1 2 3 4 5] [1.1 2.2 3.3 4.4 5.5]}

Support environment variables

// file name use_env.go
package main

import (
	"fmt"
	"github.com/guonaihong/clop"
)

type env struct {
	OmpNumThread string `clop:"env=omp_num_thread" usage:"omp num thread"`
	Path         string `clop:"env=XPATH" usage:"xpath"`
	Max          int    `clop:"env=MAX" usage:"max thread"`
}

func main() {
	e := env{}
	clop.Bind(&e)
	fmt.Printf("%#v\n", e)
}
// run
// env XPATH=`pwd` omp_num_thread=3 MAX=4 ./use_env 
// output
// main.env{OmpNumThread:"3", Path:"/home/guo", Max:4}

subcommand

package main

import (
	"fmt"
	"github.com/guonaihong/clop"
)

type add struct {
	All      bool     `clop:"-A; --all" usage:"add changes from all tracked and untracked files"`
	Force    bool     `clop:"-f; --force" usage:"allow adding otherwise ignored files"`
	Pathspec []string `clop:"args=pathspec"`
}

type mv struct {
	Force bool `clop:"-f; --force" usage:"allow adding otherwise ignored files"`
}

type git struct {
	Add add `clop:"subcommand=add" usage:"Add file contents to the index"`
	Mv  mv  `clop:"subcommand=mv" usage:"Move or rename a file, a directory, or a symlink"`
}

func main() {
	g := git{}
	clop.Bind(&g)
	fmt.Printf("git:%#v\n", g)
	fmt.Printf("git:set mv(%t) or set add(%t)\n", clop.IsSetSubcommand("mv"), clop.IsSetSubcommand("add"))

	switch {
	case clop.IsSetSubcommand("mv"):
		fmt.Printf("subcommand mv\n")
	case clop.IsSetSubcommand("add"):
		fmt.Printf("subcommand add\n")
	}
}

// run:
// ./git add -f

// output:
// git:main.git{Add:main.add{All:false, Force:true, Pathspec:[]string(nil)}, Mv:main.mv{Force:false}}
// git:set mv(false) or set add(true)
// subcommand add

Get command priority

package main

import (
	"fmt"
	"github.com/guonaihong/clop"
)

type cat struct {
	NumberNonblank bool `clop:"-b;--number-nonblank"
                             usage:"number nonempty output lines, overrides"`

	ShowEnds bool `clop:"-E;--show-ends"
                       usage:"display $ at end of each line"`
}

func main() {

	c := cat{}
	clop.Bind(&c)

	if clop.GetIndex("number-nonblank") < clop.GetIndex("show-ends") {
		fmt.Printf("cat -b -E\n")
	} else {
		fmt.Printf("cat -E -b \n")
	}
}
// cat -be 
// 输出 cat -b -E
// cat -Eb
// 输出 cat -E -b

Can only be set once

指定选项只能被设置一次,如果命令行选项,使用两次则会报错。

package main

import (
    "github.com/guonaihong/clop"
)

type Once struct {
    Debug bool `clop:"-d; --debug; once" usage:"debug mode"`
}

func main() {
    o := Once{}
    clop.Bind(&o)
}
/*
./once -debug -debug
error: The argument '-d' was provided more than once, but cannot be used multiple times
For more information try --help
*/

quick write

快速写法,通过使用固定的 short, long tag 生成短,长选项。可以和 cat 例子直观比较下。命令行选项越多,越能节约时间,提升效率。

package main

import (
    "fmt"
    "github.com/guonaihong/clop"
)

type cat struct {
	NumberNonblank bool `clop:"-c;long" 
	                     usage:"number nonempty output lines, overrides"`

	ShowEnds bool `clop:"-E;long" 
	               usage:"display $ at end of each line"`

	Number bool `clop:"-n;long" 
	             usage:"number all output lines"`

	SqueezeBlank bool `clop:"-s;long" 
	                   usage:"suppress repeated empty output lines"`

	ShowTab bool `clop:"-T;long" 
	              usage:"display TAB characters as ^I"`

	ShowNonprinting bool `clop:"-v;long" 
	                      usage:"use ^ and M- notation, except for LFD and TAB" `

	Files []string `clop:"args=files"`
}

func main() {
 	c := cat{}
	err := clop.Bind(&c)

	fmt.Printf("%#v, %s\n", c, err)
}

Advanced features

高级功能里面有一些 clop 包比较有特色的功能

Parsing flag code to generate clop code

让你爽翻天, 如果你的 command 想迁移至 clop, 但是全面众多的 flag 代码, 又不想花费太多时间在无谓的人肉 code 转换上, 这时候你就需要 clop 命令, 一行命令解决你的痛点.

1.安装 clop 命令

go get github.com/guonaihong/clop/cmd/clop

2.使用 clop 解析包含 flag 包的代码

就可以把 main.go 里面的 flag 库转成 clop 包的调用方式

clop -f main.go

main.go代码如下

package main

import "flag"

func main() {
	s := flag.String("string", "", "string usage")
	i := flag.Int("int", "", "int usage")
	flag.Parse()
}

输出代码如下

package main

import (
	"github.com/guonaihong/clop"
)

type flagAutoGen struct {
	Flag string `clop:"--string" usage:"string usage" `
	Flag int    `clop:"--int" usage:"int usage" `
}

func main() {
	var flagVar flagAutoGen
	clop.Bind(&flagVar)
}

Implementing linux command options

cat

package main

import (
	"fmt"
	"github.com/guonaihong/clop"
)

type cat struct {
	NumberNonblank bool `clop:"-c;--number-nonblank" 
	                     usage:"number nonempty output lines, overrides"`

	ShowEnds bool `clop:"-E;--show-ends" 
	               usage:"display $ at end of each line"`

	Number bool `clop:"-n;--number" 
	             usage:"number all output lines"`

	SqueezeBlank bool `clop:"-s;--squeeze-blank" 
	                   usage:"suppress repeated empty output lines"`

	ShowTab bool `clop:"-T;--show-tabs" 
	              usage:"display TAB characters as ^I"`

	ShowNonprinting bool `clop:"-v;--show-nonprinting" 
	                      usage:"use ^ and M- notation, except for LFD and TAB" `

	Files []string `clop:"args=files"`
}

func main() {

	c := cat{}
	err := clop.Bind(&c)

	fmt.Printf("%#v, %s\n", c, err)
}

/*
Usage:
    ./cat [Flags] <files> 

Flags:
    -E,--show-ends           display $ at end of each line 
    -T,--show-tabs           display TAB characters as ^I 
    -c,--number-nonblank     number nonempty output lines, overrides 
    -n,--number              number all output lines 
    -s,--squeeze-blank       suppress repeated empty output lines 
    -v,--show-nonprinting    use ^ and M- notation, except for LFD and TAB 

Args:
    <files>
*/
1164 次点击
所在节点    Go 编程语言
2 条回复
czyt
2021-06-03 22:11:11 +08:00
已经 star
guonaihong
2021-06-04 22:10:16 +08:00
@czyt 感谢, 也欢迎使用, 如有问题, 可提 issue

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/780793

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX