服务计算——CLI命令行实用程序开发实战-Agenda

1、概述

命令行实用程序并不是都象 cat、more、grep 是简单命令。go 项目管理程序,类似 java 项目管理 maven、Nodejs 项目管理程序 npm、git 命令行客户端、 docker 与 kubernetes 容器管理工具等等都是采用了较复杂的命令行。即一个实用程序同时支持多个子命令,每个子命令有各自独立的参数,命令之间可能存在共享的代码或逻辑,同时随着产品的发展,这些命令可能发生功能变化、添加新命令等。因此,符合 OCP 原则 的设计是至关重要的编程需求。

任务目标

  1. 熟悉 go 命令行工具管理项目
  2. 综合使用 go 的函数、数据结构与接口,编写一个简单命令行应用 agenda
  3. 使用面向对象的思想设计程序,使得程序具有良好的结构命令,并能方便修改、扩展新的命令,不会影响其他命令的代码
  4. 项目部署在 Github 上,合适多人协作,特别是代码归并
  5. 支持日志(原则上不使用debug调试程序)

2、GO 命令

GO命令 的官方说明并不一定是最新版本。最新说明请使用命令 go help 获取。 关于GO命令

必须了解的环境变量:GOROOT,GOPATH
项目目录与 gopath

2.1 go 命令的格式

使用:

go command [arguments]

版本(go 1.8)的命令有:

build       compile packages and dependencies
clean       remove object files
doc         show documentation for package or symbol
env         print Go environment information
bug         start a bug report
fix         run go tool fix on packages
fmt         run gofmt on package sources
generate    generate Go files by processing source
get         download and install packages and dependencies
install     compile and install packages and dependencies
list        list packages
run         compile and run Go program
test        test packages
tool        run specified go tool
version     print Go version
vet         run go tool vet on packages

2.2 go 命令分类

  1. 环境显示:version、env
  2. 构建流水线: clean、build、test、run、(publish/git)、get、install
  3. 包管理: list、get、install
  4. 杂项:fmt、vet、doc、tools …

具体命令格式与参数使用 go help [topic]

3、准备知识或资源

3.1 Golang 知识整理

这里推荐 time-track 的个人博客,它的学习轨迹与课程要求基本一致。以下是他语言学习的笔记,可用于语言快速浏览与参考:

以上仅代表作者观点,部分内容是不准确的,请用批判的态度看待网上博客。 切记:

  • GO 不是面向对象(OOP) 的。 所谓方法只是一种语法糖,它是特定类型上定义的操作(operation)
  • 指针是没有 nil 的,这可以避免一些尴尬。 p.Xv.x (p 指针, v 值) 在语义上是无区别的,但实现上是有区别的 p.x 是实现 c 语言 p->x 的语法糖
  • zero 值好重要

3.2 JSON 序列化与反序列化

参考:JSON and Go
json 包是内置支持的,文档位置:https://go-zh.org/pkg/encoding/json/

3.3 复杂命令行的处理

不要轻易“发明轮子”。为了实现 POSIX/GNU-风格参数处理,–flags,包括命令完成等支持,程序员们开发了无数第三方包,这些包可以在 godoc 找到。

  • pflag 包:https://godoc.org/github.com/spf13/pflag
  • cobra 包:https://godoc.org/github.com/spf13/cobra
  • goptions 包:https://godoc.org/github.com/voxelbrain/goptions
  • docker command 包:https://godoc.org/github.com/docker/cli/cli/command

go dead project 非常有用

这里我们选择 cobar 这个工具。

tip: 安装 cobra

  • $GOPATH/src/golang.org/x 目录下用 git clone 下载 systext 项目
  • 使用命令 go get -v github.com/spf13/cobra/cobra
  • 使用 go install github.com/spf13/cobra/cobra,安装后在 $GOBIN 下出现了 cobra 可执行程序。

Cobra 的简单使用
创建一个处理命令 agenda register -uTestUseragenda register --user=TestUser 的小程序。

简要步骤如下:

cobra init
cobra add register

需要的文件就产生了。 你需要阅读 main.gomain()root.goExecute();最后修改 register.goinit() 添加:

registerCmd.Flags().StringP("user", "u", "Anonymous", "Help message for username")

Run 匿名回调函数中添加:

username, _ := cmd.Flags().GetString("user")
fmt.Println("register called by " + username)

测试命令:

$ go run main.go register --user=TestUser
register called by TestUser

参考文档:

4、agenda 开发项目

4.1 需求描述

  • 业务需求:
    • 用户注册:
    1. 注册新用户时, 用户需设置一个唯一的用户名和一个密码。另外,还需登记邮箱及电话信息。
    2. 如果注册时提供的用户名已由其他用户使用,应反馈一个适当的出错信息;成功注册后,亦应反馈一个成功注册的信息。
    • 用户登录:
    1. 用户使用用户名和密码登录 Agenda 系统。
    2. 用户名和密码同时正确则登录成功并反馈一个成功登录的信息。否则,登录失败并反馈一个失败登录的信息。
    • 用户登出:
    1. 已登录的用户登出系统后,只能使用用户注册和用户登录功能。
  • 功能需求:设计一组命令完成 agenda 的管理,例如:
    • agenda help :列出命令说明
    • agenda register -uUserName –password pass –email=a@xxx.com :注册用户
    • agenda help register :列出 register 命令的描述
    • agenda cm … : 创建一个会议
      原则上一个命令对应一个业务功能
  • 持久化要求:
    • 使用 json 存储 User 和 Meeting 实体
    • 当前用户信息存储在 curUser.txt 中
  • 开发需求
    • 完成两条命令
  • 项目目录
    • cmd :存放命令实现代码
    • entity :存放 User 和 Meeting 对象读写与处理逻辑
    • 其他目录 : 自由添加
  • 日志服务
    • 使用 log 包记录命令执行情况

5、实验过程

5.1 安装使用 Cobra

  • $GOPATH/src/golang.org/x 目录下用 git clone 下载 systext 项目。
git clone https://github.com/golang/text
git clone https://github.com/golang/sys
  • 安装 Cobra
go get -v github.com/spf13/cobra/cobra
go install github.com/spf13/cobra/cobra
  • 初始化并添加相应指令
cobra init --pkg-name Agenda
cobra add register
cobra add login
cobra add logout

在这里插入图片描述

5.2 实现 Agenda 指令

本次实验一共完成了三条指令:用户注册、用户登录、用户登出。

  • 用户注册:
  1. 注册新用户时, 用户需设置一个唯一的用户名和一个密码。另外,还需登记邮箱及电话信息。
  2. 如果注册时提供的用户名已由其他用户使用,应反馈一个适当的出错信息;成功注册后,亦应反馈一个成功注册的信息。
register a new user, a unique username, a password, an email and a phone required

Usage:
  Agenda register [flags]

Flags:
  -c, --contact string    phone number
  -e, --email string      email address
  -h, --help              help for register
  -p, --password string   password
  -u, --username string   username

实现代码如下:

/*
Copyright © 2019 NAME HERE <EMAIL ADDRESS>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd

import (
	//"fmt"
	"github.com/github-user/Agenda/entity"
	"github.com/spf13/cobra"
)

// registerCmd represents the register command
var registerCmd = &cobra.Command{
	Use:   "register -u [username] -p [password] -e [emial] -c [phone]",
	Short: "register a new user",
	Long: "register a new user, a unique username, a password, an email and a phone required",
	Run: func(cmd *cobra.Command, args []string) {
		username, _ := cmd.Flags().GetString("username")
		password, _ := cmd.Flags().GetString("password")
		email, _ := cmd.Flags().GetString("email")
		contact, _ := cmd.Flags().GetString("contact")

		Log.SetPrefix("[Cmd]   ")
		Log.Printf("register --username=%s --password=%s --email=%v --phone=%v", username, password, email, contact)
		//fmt.Printf("[Cmd]   register --username=%s --password=%s --email=%v --phone=%v \n", username, password, email, contact)

		if err := entity.RegisterUser(username, password, email, contact); err != nil {
			Log.SetPrefix("[Error] ")
			Log.Println(err)
			//fmt.Printf("[Error] ")
			//fmt.Println(err)
		} else {
			Log.SetPrefix("[OK]    ")
			Log.Println("register successfully")
			//fmt.Println("[OK]    register successfully")
		}
		//fmt.Println("register called")
	},
}

func init() {
	rootCmd.AddCommand(registerCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// registerCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// registerCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")

	registerCmd.Flags().StringP("username", "u", "", "username")
	registerCmd.Flags().StringP("password", "p", "", "password")
	registerCmd.Flags().StringP("email", "e", "", "email")
	registerCmd.Flags().StringP("contact", "c", "", "phone")
}
  • 用户登录:
  1. 用户使用用户名和密码登录 Agenda 系统。
  2. 用户名和密码同时正确则登录成功并反馈一个成功登录的信息。否则,登录失败并反馈一个失败登录的信息。
login a user, a username and a password required

Usage:
  Agenda login -u [username] -p [password] -e [email] -c [phone] [flags]

Flags:
  -h, --help              help for login
  -p, --password string   password
  -u, --username string   username

实现代码如下:

/*
Copyright © 2019 NAME HERE <EMAIL ADDRESS>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd

import (
	//"fmt"
	"github.com/github-user/Agenda/entity"
	"github.com/spf13/cobra"
)

// loginCmd represents the login command
var loginCmd = &cobra.Command{
	Use:   "login -u [username] -p [password] -e [email] -c [phone]",
	Short: "login a user",
	Long: "login a user, a username and a password required",
	Run: func(cmd *cobra.Command, args []string) {
		username, _ := cmd.Flags().GetString("username")
		password, _ := cmd.Flags().GetString("password")

		Log.SetPrefix("[Cmd]   ")
		Log.Printf("login --username=%s --password=%s", username, password)
		//fmt.Printf("[Cmd]   login --username=%s --password=%s \n", username, password)

		if err := entity.LoginUser(username, password); err != nil {
			Log.SetPrefix("[Error] ")
			Log.Println(err)
			//fmt.Print("[Error] ")
			//fmt.Println(err)
		} else {
			Log.SetPrefix("[OK]    ")
			Log.Println("login successfully")
			//fmt.Println("[OK]    login successfully")
		}
		//fmt.Println("login called")
	},
}

func init() {
	rootCmd.AddCommand(loginCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// loginCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// loginCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")

	loginCmd.Flags().StringP("username", "u", "", "username")
	loginCmd.Flags().StringP("password", "p", "", "password")
}
  • 用户登出:
  1. 已登录的用户登出系统后,只能使用用户注册和用户登录功能。
logout a user

Usage:
  Agenda logout [flags]

Flags:
  -h, --help   help for logout

实现代码如下:

/*
Copyright © 2019 NAME HERE <EMAIL ADDRESS>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd

import (
	//"fmt"
	"github.com/github-user/Agenda/entity"
	"github.com/spf13/cobra"
)

// logoutCmd represents the logout command
var logoutCmd = &cobra.Command{
	Use:   "logout",
	Short: "logout a user",
	Long: "logout a user",
	Run: func(cmd *cobra.Command, args []string) {
		Log.SetPrefix("[Cmd]   ")
		Log.Printf("logout")
		//fmt.Println("[Cmd]   logout")

		if err := entity.LogoutUser(); err != nil {
			Log.SetPrefix("[Error] ")
			Log.Println(err)
			//fmt.Printf("[Error] ")
			//fmt.Println(err)
		} else {
			Log.SetPrefix("[OK]    ")
			Log.Println("logout successfully")
			//fmt.Println("[OK]    logout successfully")
		}
		//fmt.Println("logout called")
	},
}

func init() {
	rootCmd.AddCommand(logoutCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// logoutCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// logoutCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

5.3 测试结果

查看 Agenda 的基本信息:

go run main.go

在这里插入图片描述
用户注册:

go run main.go register -u lzz -p 123 -e 123@qq.com -c 123

在这里插入图片描述
注册成功后可以在 userList.txt 中看到用户的相关信息:
在这里插入图片描述
用户登录:

go run main.go login -u lzz -p 123

在这里插入图片描述
登录成功后可以在 curUser.txt 中看到已登录用户的相关信息:
在这里插入图片描述
用户登出:

go run main.go logout

在这里插入图片描述
在 log.log 文件中可以看到命令的执行情况:
在这里插入图片描述

项目地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值