cli - 构建强大命令行程序的工具箱 (5)

mkideal · · 1717 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。
======== cli 开源在 [github](https://github.com/mkideal/cli) 上,欢迎大家前去 star :-) 使用go get获取 ```shell go get github.com/mkideal/cli ``` [上一篇](http://studygolang.com/topics/1524)介绍了使用`cli`构建http服务的示例。本篇介绍一个用`cli`创建的实用程序 [`exp`](https://github.com/mkideal/tools/blob/master/exp/README.md) 这是一个用于求值表达式的命令行程序,使用栗子: ```shell exp 1+2 exp -e 1+2 exp "1 + 2" exp x -Dx=2.5 exp "x * y" -Dx=2 -Dy=6 exp "min(x, 4)" -Dx=3 exp "max(x, y, z)" -Dx=2 -Dy=6 -Dz=5 exp "rand() //rand in [0,10000)" exp 'rand(n)' -Dn=100 exp 'rand(1,to)' -Dto=5 exp 'sum(1,2,3)' exp 'aver(1,2,3)' exp x y x+y x-y x*y x/y x%y x^y -Dx=7 -Dy=2 exp -e x y x+y x-y x*y x/y x%y x^y -Dx=7 -Dy=2 exp 'sin(pi)' 'sin(pi/2)' exp e exp pi ``` ##代码(更完整的代码参见[exp](https://github.com/mkideal/tools/blob/master/exp/main.go)) ```go 1 package main 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "math" 7 "math/rand" 8 "os" 9 "strings" 10 "time" 11 "unicode" 12 13 "github.com/mkideal/cli" 14 "github.com/mkideal/pkg/expr" 15 ) 16 17 type argT struct { 18 cli.Helper 19 Variables map[string]float64 `cli:"D" usage:"define variables, e.g. -Dx=3 -Dy=4"` 20 OutExpr bool `cli:"e" usage:"whther output native expression" dft:"false"` 21 File string `cli:"f,file" usage:"read expr from file"` 22 Stdin bool `cli:"i,stdin" usage:"read expr from stdin" sdt:"false"` 23 24 args []string `cli:"-"` 25 } 26 27 func (argv *argT) Validate(ctx *cli.Context) error { 28 argv.args = ctx.FreedomArgs() 29 30 dataList := make([]string, 0) 31 if argv.File != "" { 32 data, err := ioutil.ReadFile(argv.File) 33 if err != nil { 34 return err 35 } 36 dataList = append(dataList, string(data)) 37 } 38 if argv.Stdin { 39 if data, err := ioutil.ReadAll(os.Stdin); err != nil { 40 return err 41 } else if len(data) > 0 { 42 dataList = append(dataList, string(data)) 43 } 44 } 45 for _, data := range dataList { 46 args := strings.Split(strings.TrimSpace(data), "\n") 47 for _, arg := range args { 48 arg = strings.TrimFunc(arg, func(r rune) bool { 49 return unicode.IsSpace(r) || r == '"' || r == '\'' 50 }) 51 if arg != "" { 52 argv.args = append(argv.args, arg) 53 } 54 } 55 } 56 return nil 57 } 58 59 func run(ctx *cli.Context, argv *argT) error { 60 rand.Seed(time.Now().UnixNano()) 61 if argv.Variables == nil { 62 argv.Variables = make(map[string]float64) 63 } 64 getter := expr.Getter(argv.Variables) 65 yellow := ctx.Color().Yellow 66 67 for k, v := range reservedWords { 68 if _, ok := getter[k]; ok { 69 return fmt.Errorf("%s is reserved word", yellow(k)) 70 } 71 getter[k] = v 72 } 73 74 for _, s := range argv.args { 75 e, err := expr.New(s, pool) 76 if err != nil { 77 return err 78 } 79 ret, err := e.Eval(getter) 80 if err != nil { 81 return err 82 } 83 if argv.OutExpr { 84 ctx.String("%s: ", s) 85 } 86 ctx.String("%G\n", ret) 87 } 88 return nil 89 } 90 91 func main() { 92 cli.Run(new(argT), func(ctx *cli.Context) error { 93 argv := ctx.Argv().(*argT) 94 if argv.Help { 95 ctx.WriteUsage() 96 return nil 97 } 98 return run(ctx, argv) 99 }, `exp - evaluate expressions 100 examples: 101 exp 1+2 102 exp -e 1+2 103 exp "1 + 2" 104 exp x -Dx=2.5 105 exp "x * y" -Dx=2 -Dy=6 106 exp "min(x, 4)" -Dx=3 107 exp "max(x, y, z)" -Dx=2 -Dy=6 -Dz=5 108 exp "rand() //rand in [0,10000)" 109 exp 'rand(n)' -Dn=100 110 exp 'rand(1,to)' -Dto=5 111 exp 'sum(1,2,3)' 112 exp 'aver(1,2,3)' 113 exp x y x+y x-y x*y x/y x%%y x^y -Dx=7 -Dy=2 114 exp -e x y x+y x-y x*y x/y x%%y x^y -Dx=7 -Dy=2`) 115 } 116 117 var reservedWords = map[string]float64{ 118 "e": math.E, 119 "E": math.E, 120 "pi": math.Pi, 121 "PI": math.Pi, 122 } ``` ## Map类型作为参数 注意到了吗?在argT的定义中定义了一个map型的变量 ```go Variables map[string]float64 `cli:"D" usage:"define variables, e.g. -Dx=3 -Dy=4"` ``` `cli` 已经支持slice和map了。用法也简单,就两种形式: -Dkey=value -D key=value 本示例中 -D 用来给变量赋值 ## 读取标准输入 示例中,argT中的Stdin为true时,将从标准输入流中读取表达式,比如 ```shell $> echo "x+y" | exp -i -Dx=2 -Dy=3 5 ``` ## FreedomArgs 在代码28行调用了`cli.Context`的一个获取自由参数数组的函数 ```go argv.args = ctx.FreedomArgs() ``` 理论式的解释FreedomArgs的含义,还不如来几个栗子 1) exp x+y -Dx=2 -Dy=3 => FreedomArgs = ["x+y"] 2) exp x+y x -D x=2 -D y=3 => FreedomArgs = ["x+y", "x"] 概括的说,在命令行程序中,不是属于flag的参数就叫FreedomArg。上面的栗子里, `x=2` `y=3` 都是属于`-D`这个flag的,所以不是Freedom参数,而 x+y x 均是。 在`exp`这个程序里,如果FreedomArgs数组由多个元素,那么美个元素都是一个表达式,会依次进行求值,每个值输出到一行。所以上面第二个栗子的输出像这样: ```shell 5 2 ``` ## 结语 这个栗子很简单,但是却构建了一个很好用的命令行表达式求值程序。在[github.com/mkideal/tools](https://github.com/mkideal/tools)中还有其他栗子。比如traffic,这个用来做http重定向。假如你打算在一台机器上部署两个网站,对外都想直接用80端口,那么trafficd可以帮你的忙。假如你为自己的两个网站分别注册了域名地址 `www.a.com` 和`www.b.com`都绑到一个IP上 x.x.x.x。然后在主机x.x.x.x上起两个端口8080.9090服务你的两个网站。此时你是需要像这样访问你的两个网站的 ``` http://www.a.com:8080 http://www.b.com:9090 ``` 现在在主机x.x.x.x上启动trafficd ``` sudo trafficd --port=80 -Mwww.a.com=www.a.com:8080 -Mwww.b.com=www.b.com:9090 ``` 这下可以不用输入8080或9090这样的端口就可以直接访问www.a.com和www.b.com了。

入群交流(和以上内容无关):Go中文网 QQ 交流群:729884609 或加微信入微信群:274768166 备注:入群;关注公众号:Go语言中文网

1717 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传