因为机缘巧合,因为希望能在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. 浏览器内核与BOM对象介绍

    BOM(Browser Object Model)对象介绍 我们都知道js有三部分组成,ECMAScript.DOM和BOM,根据宿主(浏览器)的不同,具体的表现形式也不尽相同,ie和其它浏览器也是风 ...

  2. java 学习笔记(三)ZooKeeper集群搭建实例,以及集成dubbo时的配置 (转)

    ZooKeeper集群搭建实例,以及集成dubbo时的配置 zookeeper是什么: Zookeeper,一种分布式应用的协作服务,是Google的Chubby一个开源的实现,是Hadoop的分布式 ...

  3. 寻找hive视图

    如何hive视图 1.mysql数据库 [centos@s201 ~]$ mysql -uroot -proot mysql> show databases; +---------------- ...

  4. django之表多对多建立方式、form组件、钩子函数 08

    目录 多对多三种创建方式 1.全自动(用ManyToManyField创建第三张表) 2.纯手写 3.半自动 form组件 引入 form组件的使用 forms组件渲染标签 form表单展示信息 fo ...

  5. Linux通过秘钥远程连接

    1. 秘钥生成命令 ssh-keygen 执行完上述的命令就会在root目录下生成公钥跟私钥文件 /root/.ssh/.id_rsa   私钥 /root/.ssh/.id_rsa.pub   公钥 ...

  6. docker安装redis并允许外网访问

    拉取redis镜像 docker pull redis:3.2 本地新建redis配置文件 redis.conf ,写入以下内容 #允许外网访问bind 0.0.0.0 daemonize NO pr ...

  7. What is Double 11 in China? Is it a famous festival?

    "1" means single, 11th, November is quadruple single!! What a tragedy for those single you ...

  8. java多线程批量下载文件

    多线程下载文件 平时开发中有时会用到文件下载,为了提高文件的下载速率,采用多线程下载能够达到事半功倍的效果: package test; /** * 文件下载类 * @author luweichen ...

  9. mysql5.7外网访问

    GRANT ALL PRIVILEGES ON *.* TO '账号名称'@'%' IDENTIFIED BY '密码' WITH GRANT OPTION; FLUSH PRIVILEGES; // ...

  10. vue 钩子函数的使用

    1.什么是自定义指令,有哪些钩子函数及自定义指令的使用场景 ①自定义指令是什么?以及自定义指令的使用场景 在Vue中,有很多内置指令,但是这些指令只能满足我们最基础的使用,当我们在实际项目中遇到了必须 ...