Go语言 命令行解析(二)

今天我们继续讲解Go语言中命令行,当我们在解析命令行传递的参数时通常会想用最简单的方法来解析自己行用到的命令行参数,那么urfave/cli可以帮助我们快速的解析命令行参数,它是一个简单快速的命令行包,用于在Go语言中构建命令行应用程序,目的是使开发人员能够以表达的方式编写快速分发的命令行应用程序,urfave/cli库抽象出来:FlagCommand,SubCommand等模块,用户只需要设置模块信息,参数的解析和关联就可以,帮助信息自动生成。

在C语言中可以通过getopt(int argc, char * const argv[], const char *optstring)来完成参数解析,这里不做示例,有兴趣的同学可以自己动手尝试一下。

安装urfave/cli:

go get github.com/urfave/cli

包引用方法:

import "github.com/urfave/cli" //引入cli包

urfave/cli包中几个重要的方法:

cli.NewApp()    // 创建一个cli实例
Run()       // 程序入口,执行Run后分析参数切片和路由
app.Commands    // 要执行的命令列表
app.Before  // 运行命令之前执行的内容
app.After   // 运行命令之后执行的内容
Subcommands    // 子命令

好,开始我们演示,今天的演示示例有点长,项目目录结构如下:

#tree
.
|-- commands
|   |-- api
|   |   `-- api.go        // api commands
|   `-- service
|       `-- service.go    // 服务 commands
|-- go_cli.go             // main入口
|-- go.mod                // go module管理包
|-- go.sum
`-- webservice
    |-- bin
    |   `-- webservice
    `-- webservice.go     // web服务类

首先,定义webservice类:

webservice类主要完成几个功能:构造函数,释放资源函数,ApiService方法和Service方法。

package webservice

import "fmt"

// web service 类
type WebService struct {
    Config  string
    Port    int
    LogFile string
}

// 返回web service 实例
func NewWebService(config, logFile string, port int) *WebService {

    var ws = &WebService{
        Port:    port,
        Config:  config,
        LogFile: logFile,
    }
    return ws
}

// 释放资源
func (ws *WebService) Freed() {

}

// API调用启动方式
func (ws *WebService) ApiService() {
    fmt.Println("api service")
    fmt.Printf("port : %d \n", ws.Port)
    fmt.Printf("config : %s \n", ws.Config)
    fmt.Printf("logfile : %s \n", ws.LogFile)
    // 启动http服务
}

// 常驻方式启动
func (ws *WebService) Service() {
    fmt.Println("service")
    fmt.Printf("config : %s \n", ws.Config)
    fmt.Printf("logfile : %s \n", ws.LogFile)
    // 可以理解成类似Nginx这类服务的启动
}

我们创建Api和Service两个Commands的目录,用于分开执行HTTPService和Service两种模式。

ApiService:

在日常工作中,HTTP-Api的试比较常见的获取数据的方式,我们用这个例子来看看在命令行里面如何启动和传递参数的。

package api

import (
    "fmt"
    "github.com/gzh/webservice"
    "github.com/urfave/cli"
    "os"
)

var Command = cli.Command{
    Name:    "api",
    Aliases: []string{"web_api", "webApi"}, // 命令别名
    Flags: []cli.Flag{
        // int型参数port,value默认是8080
        cli.IntFlag{
            Name:  "port",
            Value: 8080,
            Usage: "/usr/local/web_service/service api --port=8080",
        },
        // 字符型参数配置文件,默认值为空
        cli.StringFlag{
            Name:  "config",
            Value: "",
            Usage: "/usr/local/web_service/service api --config=/usr/local/web_service/config",
        },
        // 字符型日志文件参数,默认值为空
        cli.StringFlag{
            Name:  "log_file",
            Value: "",
            Usage: "/usr/local/web_service/service api --log_file=/usr/local/web_service/log/web_service.log",
        },
    },
    Action: func(c *cli.Context) error {

        var port = c.Int("port")           // 从上下文中获取端口
        var config = c.String("config")    // 从上下文中获取配置文件
        var logFile = c.String("log_file") // 从上下文中获取日志文件

        if config == "" {
            _, _ = fmt.Fprintf(os.Stderr, "config is empty!\n")
            os.Exit(100001) // 配置文件错误码
        }

        if port <= 0 {
            _, _ = fmt.Fprintf(os.Stderr, "port is empty!\n")
            os.Exit(100002) // 端口错误码
        }

        if logFile == "" {
            _, _ = fmt.Fprintf(os.Stderr, "log_file is empty!\n")
            os.Exit(100003) // 日志文件错误码
        }

        // 实例webservice,构造函数参数:config,logfile,port
        var api = webservice.NewWebService(config, logFile, port)
        defer api.Freed()

        // 启动HTTP-API服务
        api.ApiService()

        return nil
    },
}

Service:

Service类似于Nginx一样,启动后常驻执行的服务,这种在后端中比较多见,下面的示例为Service的启动和传递参数方式。

package service

import (
    "fmt"
    "github.com/gzh/webservice"
    "github.com/urfave/cli"
    "os"
)

var Command = cli.Command{
    Name: "service",
    Flags: []cli.Flag{
        // 字符型参数配置,默认值为空
        cli.StringFlag{
            Name:  "config",
            Value: "",
            Usage: "/usr/local/web_service/service api --config=/usr/local/web_service/config",
        },
        // 字符型日志文件参数,默认值为空
        cli.StringFlag{
            Name:  "log_file",
            Value: "",
            Usage: "/usr/local/web_service/service api --log_file=/usr/local/web_service/log/web_service.log",
        },
    },
    Action: func(c *cli.Context) error {

        var config = c.String("config")    // 从上下文中获取配置文件
        var logFile = c.String("log_file") // 从上下文中获取日志文件

        if config == "" {
            _, _ = fmt.Fprintf(os.Stderr, "config is empty!\n")
            os.Exit(100001) // 配置文件错误码
        }

        if logFile == "" {
            _, _ = fmt.Fprintf(os.Stderr, "log_file is empty!\n")
            os.Exit(100003) // 日志文件错误码
        }

        // 实例webservice,构造函数参数:config,logfile
        var api = webservice.NewWebService(config, logFile, 0)
        defer api.Freed()

        // 启动服务
        api.Service()

        return nil
    },
}

完成上面的几个类之后,我们可以创建go_cli.go文件开始我们的编码。主要是实现main函数,里面创建cli实例,配置参数解析相关信息、版本、描述和帮助等信息。

package main

import (
    "fmt"
    "github.com/gzh/commands/api"
    "github.com/gzh/commands/service"
    "github.com/urfave/cli"
    "os"
)

func main() {

    // 实例化命令行
    app := cli.NewApp()
    // 服务的名称
    app.Name = "WebService"
    // 服务的描述
    app.Description = "Web服务相关描述"
    // 服务的用途描述
    app.Usage = "webservice api --port=8080 --config=path --log_file=/usr/local/webservice/log/go_cli.log"
    // 服务的版本号信息
    app.Version = "1.0.0"

    // 初始化多个命令
    app.Commands = []cli.Command{
        // api命令
        api.Command,
        // service命令
        service.Command,
    }
    app.Before = func(context *cli.Context) error {
        // 实现一些逻辑
        return nil
    }

    app.After = func(context *cli.Context) error {
        // 实现一些逻辑
        return nil
    }

    var err = app.Run(os.Args)
    if err != nil {
        fmt.Println("app run fatal! err : ", err)
    }
}

运行结果:

1、编译运行Api示例:

#go build -o webservice/bin/webservice go_cli.go
#./webservice/bin/webservice api -port=8080 -config=path -log_file=/usr/local/webservice/log/go_cli.log
api service
port : 8080 
config : path 
logfile : /usr/local/webservice/log/go_cli.log 

2、编译运行Service示例:

#go build -o webservice/bin/webservice go_cli.go
#./webservice/bin/webservice service -config=path -log_file=/usr/local/webservice/log/go_cli.log
service
config : path 
logfile : /usr/local/webservice/log/go_cli.log

3、不传递任何参数的时候,会提示命令会出现HELP信息,运行如下。

#go build -o webservice/bin/webservice go_cli.go
#./webservice/bin/webservice
NAME:
   WebService - webservice api --port=8080 --config=path --log_file=/usr/local/webservice/log/go_cli.log

USAGE:
   go_cli.exe [global options] command [command options] [arguments...]

VERSION:
   1.0.0

DESCRIPTION:
   Web服务相关描述

COMMANDS:
   api
   service
   help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h     show help
   --version, -v  print the version

总结:

命令行解析上支持的比较友好,支持的内容比较全面

使用上方便,快速

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容