一款小巧, 追求开发效果的工具命令行工具包
https://github.com/guonaihong/clop
加入代码生成的思路, 可以快速帮你从 flag 包迁移至 clop
go get github.com/guonaihong/clop/cmd/clop
就可以把 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 是一款基于 struct 的命令行解析器,麻雀虽小,五脏俱全。(从零实现)
env DEBUG=xx ./proc
cat a.txt b.txt
,可以把a.txt, b.txt
散装成员归归类,收集到你指定的结构体成员里proc -d
或者长选项proc --debug
不在话下ls -ltr
是ls -l -t -r
简写形式,方便实现普通 posix 标准命令git add
,简洁的子命令注册方式,只要会写结构体就行,3,4,5 到无穷尽子命令也支持,只要你喜欢,用上 clop 就可以实现default:"1"
,支持多种数据类型,让你省去类型转换的烦恼if x!= ""
or if y!=0
浪费青春的代码go get github.com/guonaihong/clop
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"}
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}
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}
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}
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}
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]}
加上 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}
*/
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
可以使用 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]}
// 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}
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
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
指定选项只能被设置一次,如果命令行选项,使用两次则会报错。
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
*/
快速写法,通过使用固定的 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)
}
高级功能里面有一些 clop 包比较有特色的功能
让你爽翻天, 如果你的 command 想迁移至 clop, 但是全面众多的 flag 代码, 又不想花费太多时间在无谓的人肉 code 转换上, 这时候你就需要 clop 命令, 一行命令解决你的痛点.
go get github.com/guonaihong/clop/cmd/clop
就可以把 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)
}
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>
*/
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.