1. 前言
把想了很久的命令行小工具写了个demo。前几天看了 7 天仿 gin 项目,于是今天借鉴了其路由匹配方式,写出了这个demo。
思路是:创建一个类似路由的map,key值为选项,value为选项信息的结构体,结构体中保存有选项的动作函数。程序启动时实例化一个路由map并初始化,然后进行选项匹配,如果map中有这个选项的key,就执行对应的动作。思路很简单。
以下是代码,注释都有。为了管理方便我把源码拆分成了三份:main文件main.go
,route相关定义的文件route.go
,动作函数文件optionFunc.go
。
2. 代码部分
main.go
package main
import (
"fmt"
"os"
)
func main() {
//route参数信息,只有选项没有参数的可以不写参数,参数动作函数集中到了optionFunc.go文件中
//新的选项直接写在下面,动作函数handle也要写在optionFunc.go文件中
var router = route{
"-v": commandInfo{
desc: "Show version",
handle: version,
},
"-cdb": commandInfo{
desc: "批量创建文件夹,参数后写 '路径' '文件夹前缀' '创建数目'",
params: params{"路径", "文件夹前缀", "创建数目"},
handle: branchCreateDir,
},
}
//没有选项就打印帮助信心
if len(os.Args) == 1 {
printHelp(router)
return
}
// -h 选项打印帮助信息
//因为涉及到route,所以单独把 -h 摘取出来
if os.Args[1] == "-h" {
printHelp(router)
return
}
//匹配到选项就执行选项信息中的动作,不然打印提示
if commanInfo, ok := router[os.Args[1]]; ok {
commanInfo.handle(os.Args[1:])
} else {
fmt.Printf("unknown flag '%s', please use '-h' to get help info\n", os.Args[1])
}
}
// 打印帮助信息的函数
func printHelp(router route) {
fmt.Printf("Usage:\n %s [OPTIONS]\n\nOptions:\n", os.Args[0])
for k, v := range router {
if v.params == nil {
fmt.Printf(" %s\t%s\n", k, v.desc)
} else {
fmt.Printf(" %s [ ", k)
for _, item := range v.params {
fmt.Printf("%s ", item)
}
fmt.Printf("]\t%s\n", v.desc)
}
}
}
route.go
package main
type params []string
// 选项的动作函数
type handFunc func(values params)
// 保存选项的动作、参数说明和选项功能的信息
type commandInfo struct {
handle handFunc
desc string
params params
}
// 将选项和动作成对存在map里
type route map[string]commandInfo
optionFunc.go
package main
import (
"fmt"
"os"
"runtime"
"strconv"
"strings"
)
func version(params params) {
fmt.Println("hanta-tools version 0.1.0")
}
func branchCreateDir(params params) {
//参数个数或者关键参数为空直接提示错误
if len(params) != 4 || params[1] == "" || params[3] == "" {
fmt.Printf(" %s ERROR: 请输入正确的参数", os.Args[0])
return
}
//字符串转数字失败提示参数错误
num, err := strconv.Atoi(params[3])
if err != nil {
fmt.Printf(" %s ERROR: 请输入正确的文件数", os.Args[0])
return
}
//剔除路径后的 “\” 和 “/” 符号
prePath := strings.TrimRight(params[1], "\\")
prePath = strings.TrimRight(prePath, "/")
for i := 1; i <= num; i++ {
var dirPath = ""
//根据不同系统平台来拼接完整路径
if runtime.GOOS == "windows" {
dirPath = fmt.Sprintf("%s\\%s%d", prePath, params[2], i)
} else {
dirPath = fmt.Sprintf("%s/%s%d", prePath, params[2], i)
}
err := os.Mkdir(dirPath, 0666)
//将创建失败信息完整打印到控制台
if err != nil {
fmt.Printf(" %s ERROR: %s", os.Args[0], err)
break
}
}
}
3. 效果
Windows平台编译后运行效果:
Centos 7.6编译后运行效果: