因为机缘巧合,因为希望能在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. Spring 事务相关

    事务类型 数据库事务类型有本地事务和分布式事务: 本地事务:就是普通事务,能保证单台数据库上的操作的ACID,被限定在一台数据库上: 分布式事务:涉及两个或多个数据库源的事务,即跨越多台同类或异类数据 ...

  2. linux 命令格式和帮助

    命令的格式: command [options] [arguments] command:命令 options:  --单词全称   或   -单字简称 如: ls --all 等于     ls - ...

  3. python中open与with open的区别

    读写文件是最常见的IO操作.Python内置了读写文件的函数,用法和C是兼容的.在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开 ...

  4. 修改Jenkins目录

    注意:在Jenkins运行时是不能更改的. 请先将Jenkins停止运行. 1.windows环境下更改JENKINS的主目录 Windows环境中,Jenkins主目录默认在C:\Documents ...

  5. 【51nod 1245】Binomial Coefficients Revenge

    题目大意 C(M,N) = M! / N! / (M - N)! (组合数).给出M和质数p,求C(M,0), C(M,1)......C(M,M)这M + 1个数中,有多少数不是p的倍数,有多少是p ...

  6. JS单线程和异步

    线程和单线程的概念: 线程:是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同 ...

  7. 【C#-批量插入数据到数据库】DataTable数据批量插入数据的库三种方法:SqlCommand.EcecuteNonQurery(),SqlDataAdapter.Update(DataTable) ,SqlBulkCopy.WriteToServer(Datatable)

    第一种方法:使用SqlCommand.EcecuteNonQurery()  效率最慢 第二种方法:使用SqlDataAdapter.Update(DataTable)   效率次之 第三种方法:使用 ...

  8. kubernetes的搭建以及dashboard页面的启动

    ###查看kubernetes状态 ``` kubectl get pods -A #查看相关状态 kubectl get cs #查看k8s的ready状态 kubectl get node #查看 ...

  9. ISO15765

    常用的缩略词 ISO15765网络层服务 协议功能 a)发送/接收最多4095个字节的数据信息: b)报告发送/接收完成状态. 网络层内部传输服务,CAN总线上的数据帧没帧只能传输8个字节,ISO 为 ...

  10. Android_(控件)使用AlertDialog实现点击Button显示出多选框

    单击"更多"按钮,显示出多选框 运行截图: 程序结构 (本想通过Button中android:background使用drawable资源下的图片作为按钮背景,设计太丑就去掉了Σ( ...