跳至主要內容

编写命令行工具

Mr.Hope...大约 4 分钟

编写命令行工具

环境准备完毕,接下来实现java命令的的第一种用法。

创建目录

创建cmd.go

Go源文件一般以.go作为后缀,文件名全部小写,多个单词之间使用下划线分隔。Go语言规范要求Go源文件必须使用UTF-8编码,详见https://golang.org/ref/spec

结构体存储cmd选项

在文件中定义cmd中java命令需要的选项和参数

package ch01

// author:郜宇博
type Cmd struct {
	// 标注是否为 --help
	helpFlag bool
	//标注是否为 --version
	versionFlag bool
	//选项
	cpOption string
	//主类名,或者是jar文件
	class string
	//参数
	args []string
}

Go语言标准库包

由于要处理的命令行,因此将使用到flag()函数,此函数为Go的标准库包之一。

Go语言的标准库以包的方式提供支持,下表列出了Go语言标准库中常见的包及其功能。

Go语言标准库包名功 能
bufio带缓冲的 I/O 操作
bytes实现字节操作
container封装堆、列表和环形列表等容器
crypto加密算法
database数据库驱动和接口
debug各种调试文件格式访问及调试功能
encoding常见算法如 JSON、XML、Base64 等
flag命令行解析
fmt格式化操作
goGo语言的词法、语法树、类型等。可通过这个包进行代码信息提取和修改
htmlHTML 转义及模板系统
image常见图形格式的访问及生成
io实现 I/O 原始访问接口及访问封装
math数学库
net网络库,支持 Socket、HTTP、邮件、RPC、SMTP 等
os操作系统平台不依赖平台操作封装
path兼容各操作系统的路径操作实用函数
pluginGo 1.7 加入的插件系统。支持将代码编译为插件,按需加载
reflect语言反射支持。可以动态获得代码中的类型信息,获取和修改变量的值
regexp正则表达式封装
runtime运行时接口
sort排序接口
strings字符串转换、解析及实用函数
time时间接口
text文本模板及 Token 词法器

flag()函数

Golang标准库文档open in new window

eg:flag.TypeVar()

基本格式如下: flag.TypeVar(Type指针, flag名, 默认值, 帮助信息) 例如我们要定义姓名、年龄、婚否三个命令行参数,我们可以按如下方式定义:

var name string
var age int
var married bool
var delay time.Duration
flag.StringVar(&name, "name", "张三", "姓名")
flag.IntVar(&age, "age", 18, "年龄")
flag.BoolVar(&married, "married", false, "婚否")
flag.DurationVar(&delay, "d", 0, "时间间隔")

接收处理用户输入的命令行指令

创建parseCmd()函数,实现接受处理用户输入的命令行指令

func parseCmd() *Cmd {
	cmd := &Cmd{}

	flag.Usage = printUsage
	flag.BoolVar(&cmd.helpFlag, "help", false, "print help message")
	flag.BoolVar(&cmd.helpFlag, "?", false, "print help message")
	flag.BoolVar(&cmd.versionFlag, "version", false, "print version and exit")
	flag.StringVar(&cmd.cpOption, "classpath", "", "classpath")
	flag.StringVar(&cmd.cpOption, "cp", "", "classpath")
	flag.Parse()

	args := flag.Args()
	if len(args) > 0 {
		cmd.class = args[0]
		cmd.args = args[1:]
	}

	return cmd
}

func printUsage() {
	fmt.Printf("Usage: %s [-options] class [args...]\n", os.Args[0])
	//flag.PrintDefaults()
}

首先设置flag.Usage变量,把printUsage()函数赋值给它; 然后调 用flag包提供的各种Var函数设置需要解析的选项; 接着调用 Parse()函数解析选项。

如果Parse()函数解析失败,它就调用 printUsage()函数把命令的用法打印到控制台。

如果解析成功,调用flag.Args()函数可以捕获其他没有被解析 的参数。其中第一个参数就是主类名,剩下的是要传递给主类的参数。

测试

与cmd.go文件一样,main.go文件的包名也是main。在Go 语言中,main是一个特殊的包,这个包所在的目录(可以叫作任何 名字)会被编译为可执行文件。Go程序的入口也是main()函数,但 是不接收任何参数,也不能有返回值。

测试代码如下:

package main

import "fmt"

func main() {
	cmd := parseCmd()
	if cmd.versionFlag {
		//模拟输出版本
		fmt.Println("version 0.0.1")
	} else if cmd.helpFlag || cmd.class == "" {
		printUsage()
	} else {
		startJVM(cmd)
	}
}

// 模拟启动JVM
func startJVM(cmd *Cmd) {
	fmt.Printf("classpath:%s class:%s args:%v\n",
		cmd.cpOption, cmd.class, cmd.args)
}

main()函数先调用ParseCommand()函数解析命令行参数,如 果一切正常,则调用startJVM()函数启动Java虚拟机。如果解析出现错误,或者用户输入了-help选项,则调用PrintUsage()函数打印出帮助信息。如果用户输入了-version选项,则输版本信息。因为我们还没有真正开始编写Java虚拟机,所以startJVM()函数暂时只是打印一些信息而已。

在终端:

go install jvmgo\ch0

此时在工作空间的bin目录中会生成ch01.exe的文件,运行:结果如下