- project address
- https://gitee.com/guonaihong/clop
- https://github.com/guonaihong/clop
- The content of this update
- Optimization: SetVersion interface
- Added: issues template
clop
clop (Command Line Option Parse) is a struct-based command line parser. Although the sparrow is small, it has all the internal organs (implemented from scratch).
feature
- Support environment variable binding
env DEBUG=xx ./proc
- Support parameter collection , you can classify bulk members and collect them into the structure members you specify
cat a.txt b.txt
a.txt, b.txt
- Supports short
proc -d
or longproc --debug
options - POSIX style command line support, support command combination
ls -ltr
is als -l -t -r
shorthand form, which is convenient to implement common POSIX standard commands - Subcommand (
subcommand
) support, easy to implement git style subcommands , simple subcommand registration method, as long as you can write the structure, 3,4,5 to infinite subcommands are also supported, as long as you like, you can use clop to achievegit add
- Default value support
default:"1"
, support for multiple data types, save you the trouble of type conversion - Intimate repeat command error
- Strictly short options, long options report an error. Avoid ambiguous options
- Validation mode support, no need to write a bunch of code or waste your youth
if x!= ""
if y!=0
- You can get the command priority level, which is convenient for setting command aliases
- Parse the flag package code to generate clop code
content
- Installation
- Quick start
- example
- Implementing linux command options
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 . SetVersion ( "v0.2.0" ) clop . SetAbout ( "This is a simple example demo" ) 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
With the greedy attribute, array greedy writing is supported. Similar to the join command.
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 output &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
You can use the default tag to set the default value, the common type is written directly, and the composite type is represented by json
package mainimport ( "fmt""github.com/guonaihong/clop" )typedefaultExamplestruct { Intint`default:"1"`Float64float64`default:"3.64"`Float32float32`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]"` : =de() { mainfunc } 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
custom environment variable name
// 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}
Quick writing of environment variables
Using env tag will generate an environment variable name based on the structure name. The rule is the camel case command name, which is changed to uppercase and underscore.
// file name use_env.go package main import ( "fmt" "github.com/guonaihong/clop" ) type env struct { OmpNumThread string `clop:"env" usage:"omp num thread"` Xpath string `clop:"env" usage:"xpath"` Max int `clop:"env" 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", Xpath:"/home/guo", Max:4}
subcommand
Sub command implementation method 1
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"` }funcmain() { 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
Sub command implementation method 2
The second method of using clop to implement subcommands. As long as the subcommand structure implements SubMain
the method, the method clop library will automatically call it for you. It is unnecessary to write a bunch of if else judgments in main (relative to method 1), especially This method is recommended when there are too many subcommands.
package mainimport ( "fmt""github.com/guonaihong/clop" )typeaddstruct { Allbool`clop:"-A; --all" usage:"add changes from all tracked and untracked files"`Forcebool`clop:"-f; --force" usage:"allow adding otherwise ignored files"`Pathspec []string`clop:"args=pathspec"` boolForce{ structmvtype }// clop will automatically call this function// when the add subcommand is set() { SubMain)add*a(func } `clop:"-f; --force" usage:"allow adding otherwise ignored files"` } func ( m * mv ) SubMain () { // when the mv subcommand is set // clop will automatically call this function } 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 ) }
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
The specified option can only be set once, if the command line option is used twice, an error will be reported.
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
Fast way to generate short and long options by using fixed short, long tags. It can be visually compared with the cat example. The more command-line options you have, the more time you can save and more efficiency.
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) }
Multi structure series
Multiple structure concatenation function. Multiple structures are unified into one command line view
If the command line parsing is to go into multiple (>=2) structures, you can use the structure concatenation function, the first few structures use clop.Register()
interfaces, and the last structure use clop.Bind()
functions.
/* ┌────────────────┐ │ │ │ │ │ ServerAddress │ ┌─────────────────────┐ ├────────────────┤ │ │ │ │ ──────────────────► │ │ │ │ │ clop.MustRegitser()│ │ Rate │ │ │ │ │ └─────────────────────┘ └────────────────┘ ┌────────────────┐ │ │ │ ThreadNum │ │ │ ┌─────────────────────┐ │ │ │ │ ├────────────────┤ ──────────────────► │ │ │ │ │ clop.Bind() │ │ OpenVad │ │ │ │ │ │ │ └────────────────┘ └─────────────────────┘ */ type Server struct { ServerAddress string `clop:"long" usage:"Server address"` Rate time.Duration `clop:"long" usage:"The speed at which audio is sent"` } type Asr struct{ ThreadNum int `clop:"long" usage:"thread number"` OpenVad bool `clop:"long" usage:"open vad"` } func main () { asr := Asr {} ser := Server {} clop . MustRegister ( & asr ) clop . Bind ( & ser ) } // You can use the following command line parameters to test the effect // ./example --server-address", ":8080", "--rate", "1s", " --thread-num", "20", "--open-vad"
Advanced features
There are some unique functions of the clop package in the advanced functions
Parsing flag code to generate clop code
It makes you feel great. If your command wants to migrate to clop, but you are faced with a lot of flag codes, and you don’t want to spend too much time on unnecessary code conversion, then you need the clop command, a one-line command to solve your pain points .
1. Install the clop command
go get github.com/guonaihong/clop/cmd/clop
2. Use clop to parse the code containing the flag package
You can convert the flag library in main.go into the calling method of the clop package
clop -f main.go
main.go
code show as below
package main import "flag" func main() { s := flag.String("string", "", "string usage") i := flag.Int("int", "", "int usage") flag.Parse() }
The output code is as follows
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> */