因为机缘巧合,因为希望能在VPS中使用百度网盘,了解到了一个开源的项目BaiduPCS-Go,可以用来直接存取访问百度网盘,做的相当不错

而且看ISSUES,作者可能还是个学生,很强的样子。稍微看了下代码,发现了一个很不错的用来写命令行程序CLI的框架,也是在Github上开源的,因为Golang主要是用来写这个的,所以感觉比较有用的样子,学习一下,并且稍微做了个笔记。

这个框架就是

github.com/urfave/cli

稍微整理了下具体使用的方式

1,最初的版本,如何引入等等

package main
import (
"fmt"
"log"
"os"
"github.com/urfave/cli"
) func main() {
app := cli.NewApp()
app.Name = "boom"
app.Usage = "make an explosive entrance"
app.Action = func(c *cli.Context) error {
fmt.Println("boom! I say!")
return nil
} err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}

这里的 app.Action 中间的,就是CLI执行(回车)后的操作。乍看没啥东西,其实这个时候已经封装了 -help -version等等的方法了

执行这个GO程序,控制台输出 boom! I say! 如果执行 main -help 则输出命令行的帮助,类似下面的样子

NAME:
boom - make an explosive entrance USAGE:
002 [global options] command [command options] [arguments...] VERSION:
0.0.0 COMMANDS:
help, h Shows a list of commands or help for one command GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version

2,慢慢深入,接下来有参数,也就是执行的时候 XXX.EXE Arg这个Arg部分如何处理

package main

import (
"fmt"
"log"
"os" "github.com/urfave/cli"
) func main() {
app := cli.NewApp() app.Action = func(c *cli.Context) error {
//获取第一个参数
fmt.Printf("Hello %q", c.Args().Get(0))
return nil
} err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}

这时候输入 参数1 ,则控制台返回 hello “1”;

3,关于FLAG

有很多命令行程序有flag 比如linux下的常用的 netstat -lnp 等等

package main

import (
"fmt"
"log"
"os" "github.com/urfave/cli"
) func main() {
app := cli.NewApp() app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang",
Value: "english",
Usage: "language for the greeting",
},
} app.Action = func(c *cli.Context) error {
name := "Nefertiti"
if c.NArg() > 0 {
name = c.Args().Get(0)
}
if c.String("lang") == "spanish" {
fmt.Println("Hola", name)
} else {
fmt.Println("Hello", name)
}
return nil
} err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}

这个时候我们执行 002.go –lang english 控制台会输出  Hello Nefertiti;执行002.go –lang spanish,则会输出 Hola Nefertiti。

在程序里,通过 判断 c.String(“lang”) 来决定程序分叉

当然程序稍微修改一下,通过一个属性也能对参数赋值

app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang",
Value: "english",
Usage: "language for the greeting",
Destination: &language, //取到的FLAG值,赋值到这个变量
},
}

使用的时候只要用 language 来判断就行了

4.关于commond和subcommand

package main

import (
"fmt"
"log"
"os" "github.com/urfave/cli"
) func main() {
app := cli.NewApp() app.Commands = []cli.Command{
{
Name: "add",
Aliases: []string{"a"},
Usage: "add a task to the list",
Action: func(c *cli.Context) error {
fmt.Println("added task: ", c.Args().First())
return nil
},
},
{
Name: "complete",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) error {
fmt.Println("completed task: ", c.Args().First())
return nil
},
},
{
Name: "template",
Aliases: []string{"t"},
Usage: "options for task templates",
Subcommands: []cli.Command{
{
Name: "add",
Usage: "add a new template",
Action: func(c *cli.Context) error {
fmt.Println("new task template: ", c.Args().First())
return nil
},
},
{
Name: "remove",
Usage: "remove an existing template",
Action: func(c *cli.Context) error {
fmt.Println("removed task template: ", c.Args().First())
return nil
},
},
},
},
} err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}

上面的例子里面,罗列了好多个commond以及subcommand,通过定义这些,我们能实现 类似

可执行程序 命令 子命令的操作,比如 App.go add 123 控制台输出 added task:  123

只要遵循框架,这个时候这些命令(Command)以及FLAG的帮助说明都是自动生成的。比如上面这个程序的help,控制台会输出所有使用方式

类似

NAME:
002 - A new cli application USAGE:
002 [global options] command [command options] [arguments...] VERSION:
0.0.0 COMMANDS:
add, a add a task to the list
complete, c complete a task on the list
template, t options for task templates
help, h Shows a list of commands or help for one command GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version

最后,稍微扩张一下,类似MYSQL,FTP,TELNET等工具,很多控制台程序,都是进入类似一个自己的运行界面,这样其实CLI本身因为并没有中断,可以保存先前操作的信息。

所以比如FTP,TELNET,Mysql这种需要权限的工具,广泛使用。这时,我们往往只需要用户登陆一次,就可以继续执行上传下载查询通讯等等的后续操作。

废话不多说,直接上例子

package main

import (
"fmt"
"log"
"os"
"strings"
"bufio"
"github.com/urfave/cli"
) func main() {
app := cli.NewApp() app.Action = func(c *cli.Context) {
if c.NArg() != 0 {
fmt.Printf("未找到命令: %s\n运行命令 %s help 获取帮助\n", c.Args().Get(0), app.Name)
return
} var prompt string prompt = app.Name + " > "
L:
for {
var input string
fmt.Print(prompt)
// fmt.Scanln(&input) scanner := bufio.NewScanner(os.Stdin)
scanner.Scan() // use `for scanner.Scan()` to keep reading
input = scanner.Text()
//fmt.Println("captured:",input)
switch input {
case "close":
fmt.Println("close.")
break L
default:
}
//fmt.Print(input)
cmdArgs := strings.Split(input, " ")
//fmt.Print(len(cmdArgs))
if len(cmdArgs) == 0 {
continue
} s := []string{app.Name}
s = append(s,cmdArgs...) c.App.Run(s) } return
} app.Commands = []cli.Command{
{
Name: "add",
Aliases: []string{"a"},
Usage: "add a task to the list",
Action: func(c *cli.Context) error {
fmt.Println("added task: ", c.Args().First())
return nil
},
},
{
Name: "complete",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) error {
fmt.Println("completed task: ", c.Args().First())
return nil
},
},
{
Name: "template",
Aliases: []string{"t"},
Usage: "options for task templates",
Subcommands: []cli.Command{
{
Name: "add",
Usage: "add a new template",
Action: func(c *cli.Context) error {
fmt.Println("new task template: ", c.Args().First())
return nil
},
},
{
Name: "remove",
Usage: "remove an existing template",
Action: func(c *cli.Context) error {
fmt.Println("removed task template: ", c.Args().First())
return nil
},
},
},
},
} err := app.Run(os.Args) if err != nil {
log.Fatal(err)
}
}

这里在Action里面,其实加入了一个死循环,一直在听取程序的输入,直接执行(回车)的话,其实进入一个类似MySQL或者FTP类似的命令行界面。等待用户的进一步输入。
然后读取这个输入,通过调用原来的框架的app.Run(os.Args)来处理需求逻辑。

上述所有基本涵盖了命令行的所有可能的形式,以后就可以按照这个框架写出起码帮助感觉很正式的程序了。

Golang的一个CLI框架的更多相关文章

  1. 使用golang写一个redis-cli

    使用golang写一个redis-cli 0. redis通信协议 redis的客户端(redis-cli)和服务端(redis-server)的通信是建立在tcp连接之上, 两者之间数据传输的编码解 ...

  2. Golang最强大的访问控制框架casbin全解析

    Golang最强大的访问控制框架casbin全解析 Casbin是一个强大的.高效的开源访问控制框架,其权限管理机制支持多种访问控制模型.目前这个框架的生态已经发展的越来越好了.提供了各种语言的类库, ...

  3. 如何使用Golang实现一个API网关

    你是否也存在过这样的需求,想要公开一个接口到网络上.但是还得加点权限,否则被人乱调用就不好了.这个权限验证的过程,最好越简单越好,可能只是对比两个字符串相等就够了.一般情况下我们遇到这种需要,就是在函 ...

  4. 使用Prometheus监控Golang服务-基于YoyoGo框架

    Prometheus Prometheus是一个非常棒的工具,结合grafana能够让我在不写代码,或者少写代码的情况下搭建一套有效的监控体系.这里介绍一下Prometheus监控golang程序的方 ...

  5. Go语言笔记[实现一个Web框架实战]——EzWeb框架(一)

    Go语言笔记[实现一个Web框架实战]--EzWeb框架(一) 一.Golang中的net/http标准库如何处理一个请求 func main() { http.HandleFunc("/& ...

  6. Golang语言系列-17-Gin框架

    Gin框架 Gin框架简介 package main import ( "github.com/gin-gonic/gin" "io" "net/ht ...

  7. Fastflow——基于golang的轻量级工作流框架

    Fastflow 是什么?用一句话来定义它:一个 基于golang协程.支持水平扩容的分布式高性能工作流框架. 它具有以下特点: 易用性:工作流模型基于 DAG 来定义,同时还提供开箱即用的 API, ...

  8. 如何实现一个php框架系列文章【开篇】

    1.本系列文章的目的 实现一个小而美的产品级别php框架 自己动手实现一个新框架仅用于学习交流,不打算替代市面上现有的其他主流框架. 2. 我要一个怎样的PHP框架 简单实用,安全优雅,博采众长 安装 ...

  9. 第一个web框架tornado

    简介 tornado,是我学到的第一个web框架是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本.这个 Web 框架看起来有些像web.py 或者 Google ...

随机推荐

  1. IIS无法启动解决方案

    1.第一步 2.第二步

  2. pycharm运行程序,总是出现IPthony界面(IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help. PyDev console: using IPython 6.2.1)

    解决方式如下: 取消即可.

  3. jquery之闭包

    闭包 常见形式是函数A里面定义一个函数B,并返回函数体的引用,很抽象是不是,具体代码如下: function wenwa() { ; function cj() { console.log(" ...

  4. Hadoop-No.11之元数据

    元数据的重要性 三个重要理由,让我们不得不在意元数据 元数据允许用户通过一张表的高一级逻辑抽象,而不是HDFS中文件的简单几何,或者HBase中的表来与数据交互.这意味着用户不比关心数据是如何存储的, ...

  5. 51 Nod 1636 教育改革(dp)

    1636 教育改革  题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题  收藏  关注 最近A学校正在实施教育改革. 一个学年由n天 ...

  6. k8s集群节点更换ip 或者 k8s集群添加新节点

    1.需求情景:机房网络调整,突然要回收我k8s集群上一台node节点机器的ip,并调予新的ip到这台机器上,所以有了k8s集群节点更换ip一说:同时,k8s集群节点更换ip也相当于k8s集群添加新节点 ...

  7. AtCoder AGC038D Unique Path (图论)

    题目链接 https://atcoder.jp/contests/agc038/tasks/agc038_d 题解 orz zjr神仙做法 考虑把所有\(C_i=0\)的提示的两点连边,那么连完之后的 ...

  8. 记一次关于springboot的netty版本冲突问题

    冲突的地放其实很多,大概都是类似,找不到哪个方法了: 类似于: Error starting ApplicationContext. To display the conditions report ...

  9. 白鹭引擎EUI做H5活动 入门篇

    前言:本学习文档的目的是为了实现h5,或者简单的h5游戏,比如说,我们要实现一个可以左右,或者上下移动的场景的h5,在场景移动的过程中,会有相应的动画或者操作,我们通过 js 也可以实现,但是为了流畅 ...

  10. 两台Linux服务器之间的文件传输

    最近工作中有这样一个需求,需要将A服务器上的文件传到B服务器. 本来想用Java开发,但一想Java开发周期长,应对这样一个小需求没必要用Java,最后选择了Shell脚本,相关代码如下: #!/bi ...