github地址
Go Online地址
命令行实用程序并不是都象 cat、more、grep 是简单命令。go 项目管理程序,类似 java 项目管理 maven、Nodejs 项目管理程序 npm、git 命令行客户端、 docker 与 kubernetes 容器管理工具等等都是采用了较复杂的命令行。即一个实用程序同时支持多个子命令,每个子命令有各自独立的参数,命令之间可能存在共享的代码或逻辑,同时随着产品的发展,这些命令可能发生功能变化、添加新命令等。因此,符合 OCP 原则 的设计是至关重要的编程需求。
任务目标
GO命令 的官方说明并不一定是最新版本。最新说明请使用命令 go help
获取。 关于GO命令
必须了解的环境变量:GOROOT,GOPATH
项目目录与 gopath
使用:
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
具体命令格式与参数使用 go help [topic]
这里推荐 time-track 的个人博客,它的学习轨迹与课程要求基本一致。以下是他语言学习的笔记,可用于语言快速浏览与参考:
以上仅代表作者观点,部分内容是不准确的,请用批判的态度看待网上博客。 切记:
p.X
与 v.x
(p 指针, v 值) 在语义上是无区别的,但实现上是有区别的 p.x
是实现 c 语言 p->x
的语法糖参考:JSON and Go
json 包是内置支持的,文档位置:https://go-zh.org/pkg/encoding/json/
不要轻易“发明轮子”。为了实现 POSIX/GNU-风格参数处理,–flags,包括命令完成等支持,程序员们开发了无数第三方包,这些包可以在 godoc 找到。
go dead project 非常有用
这里我们选择 cobar 这个工具。
tip: 安装 cobra
go get -v github.com/spf13/cobra/cobra
下载过程中,会出提示如下错误Fetching https://golang.org/x/sys/unix?go-get=1
https fetch failed: Get https://golang.org/x/sys/unix?go-get=1: dial tcp 216.239.37.1:443: i/o timeout
这是熟悉的错误,请在 $GOPATH/src/golang.org/x
目录下用 git clone
下载 sys
和 text
项目,然后使用 go install github.com/spf13/cobra/cobra
, 安装后在 $GOBIN
下出现了 cobra 可执行程序。
Cobra 的简单使用
创建一个处理命令 agenda register -uTestUser
或 agenda register --user=TestUser
的小程序。
简要步骤如下:
cobra init
cobra add register
需要的文件就产生了。 你需要阅读 main.go
的 main()
; root.go
的 Execute()
; 最后修改 register.go
, init()
添加:
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
参考文档:
由于潘老师降低了要求,只要求能实现两个指令就可以了。
我实现了一下三个指令:
cmd文件夹
里面的go程序(除了root.go)都是根命令的子命令,定义了命令的逻辑和参数
拿login.go举例
/*
Copyright © 2019 NAME HERE
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"
"agenda/entity"
"github.com/spf13/cobra"
)
// loginCmd represents the login command
var loginCmd = &cobra.Command{
Use: "login",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("login called")
_username,_ := cmd.Flags().GetString("user")
_password,_ := cmd.Flags().GetString("password")
users := entity.ReadUsers()
for _,user := range(users) {
if _username == user.Username && _password == user.Password {//匹配
entity.SetCurrentUserName(_username)
fmt.Println("log in succeed!")
fmt.Println("Welcome!"+_username)
return
} else if _username == user.Username && _password != user.Password {
fmt.Println("Wrong Password!")
return
}
}
fmt.Println("No such user, please input a registered username and password")
return
},
}
func init() {
loginCmd.Flags().StringP("user","u","","log in")
loginCmd.Flags().StringP("password","p","","log in")
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")
}
用cobra进行初始化之后主要修改loginCmd的Run方法以及init方法实现登录的逻辑。
entity文件夹
负责与数据库的交互以及存储数据
CurrentUser.txt存储了登录了的用户的Username
Users.txt存储了已经注册的所有用户的信息(json格式)
data.go负责交互的方法
package entity
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
type User struct{
Username string
Password string
Email string
Phone string
}
func checkerr(err error){
if err != nil {
fmt.Println(err)
}
}
func GetCurrentUserName()(username string){
dir,err := os.Getwd()
checkerr(err)
b,err := ioutil.ReadFile(dir+"/entity/currentUser.txt")
checkerr(err)
username = string(b)
return username
}
func SetCurrentUserName(username string){
dir,err := os.Getwd()
checkerr(err)
b := []byte(username)
err = ioutil.WriteFile(dir+"/entity/currentUser.txt",b,0777)
checkerr(err)
}
func ReadUsers()(user []User){
dir,err := os.Getwd()
checkerr(err)
b,err := ioutil.ReadFile(dir+"/entity/Users.txt")
var users []User
json.Unmarshal(b,&users)
return users
}
func WriteUsers(users []User){
dir,err := os.Getwd()
checkerr(err)
data,err := json.Marshal(users)
checkerr(err)
b := []byte(data)
err = ioutil.WriteFile(dir+"/entity/Users.txt",b,0777)
checkerr(err)
}
go run main.go
注册用户 go run main.go register -u xxx -p xxx -e xxx -t xxx
参数都是可选类型的
注册完Users.txt会更新
登录 go run main.go login -u xxx -p xxx
如果Users.txt里面没有用户信息则会报错
登陆完会发现CurrentUser.txt出现了该用户的名字。
登出go run main.go logout
成功登出
login.go
/*
Copyright © 2019 NAME HERE
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"
"agenda/entity"
"github.com/spf13/cobra"
)
// loginCmd represents the login command
var loginCmd = &cobra.Command{
Use: "login",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("login called")
_username,_ := cmd.Flags().GetString("user")
_password,_ := cmd.Flags().GetString("password")
users := entity.ReadUsers()
for _,user := range(users) {
if _username == user.Username && _password == user.Password {//匹配
entity.SetCurrentUserName(_username)
fmt.Println("log in succeed!")
fmt.Println("Welcome!"+_username)
return
} else if _username == user.Username && _password != user.Password {
fmt.Println("Wrong Password!")
return
}
}
fmt.Println("No such user, please input a registered username and password")
return
},
}
func init() {
loginCmd.Flags().StringP("user","u","","log in")
loginCmd.Flags().StringP("password","p","","log in")
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")
}
logout.go
/*
Copyright © 2019 NAME HERE
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"
"agenda/entity"
"github.com/spf13/cobra"
)
// logoutCmd represents the logout command
var logoutCmd = &cobra.Command{
Use: "logout",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("log out called")
entity.SetCurrentUserName("")
fmt.Println("log out succeed!")
},
}
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")
}
register.go
/*
Copyright © 2019 NAME HERE
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"
"agenda/entity"
"github.com/spf13/cobra"
)
// registerCmd represents the register command
var registerCmd = &cobra.Command{
Use: "register",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("register called")
_username,_ := cmd.Flags().GetString("user")
_password,_ := cmd.Flags().GetString("password")
_email,_ := cmd.Flags().GetString("email")
_phone,_ := cmd.Flags().GetString("phone")
fmt.Println("username:"+_username+" password:"+_password+" email:"+_email+" phone:"+_phone)
if _username == "default user" {
fmt.Println("username can not be empty")
return
}
users := entity.ReadUsers()
for i := 0; i < len(users); i++ {
if users[i].Username == _username {
fmt.Println("this username has been registered")
return
}
}
newUser := entity.User{_username,_password,_email,_phone}
users = append(users,newUser)
entity.WriteUsers(users)
},
}
func init() {
registerCmd.Flags().StringP("user","u","default user","Help message for username")
registerCmd.Flags().StringP("password","p","123456","password of user")
registerCmd.Flags().StringP("email","e","[email protected]","email of user")
registerCmd.Flags().StringP("phone","t","12345647897","phone number of user")//telephone
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")
}
root.go
/*
Copyright © 2019 NAME HERE
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"
"os"
"github.com/spf13/cobra"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
)
var cfgFile string
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "agenda",
Short: "A brief description of your application",
Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cmd.yaml)")
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Search config in home directory with name ".cmd" (without extension).
viper.AddConfigPath(home)
viper.SetConfigName(".agenda")
}
viper.AutomaticEnv() // read in environment variables that match
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
fmt.Println("Using config file:", viper.ConfigFileUsed())
}
}
data.go
package entity
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
type User struct{
Username string
Password string
Email string
Phone string
}
func checkerr(err error){
if err != nil {
fmt.Println(err)
}
}
func GetCurrentUserName()(username string){
dir,err := os.Getwd()
checkerr(err)
b,err := ioutil.ReadFile(dir+"/entity/currentUser.txt")
checkerr(err)
username = string(b)
return username
}
func SetCurrentUserName(username string){
dir,err := os.Getwd()
checkerr(err)
b := []byte(username)
err = ioutil.WriteFile(dir+"/entity/currentUser.txt",b,0777)
checkerr(err)
}
func ReadUsers()(user []User){
dir,err := os.Getwd()
checkerr(err)
b,err := ioutil.ReadFile(dir+"/entity/Users.txt")
var users []User
json.Unmarshal(b,&users)
return users
}
func WriteUsers(users []User){
dir,err := os.Getwd()
checkerr(err)
data,err := json.Marshal(users)
checkerr(err)
b := []byte(data)
err = ioutil.WriteFile(dir+"/entity/Users.txt",b,0777)
checkerr(err)
}
main.go
/*
Copyright © 2019 NAME HERE
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 main
import "agenda/cmd"
func main() {
cmd.Execute()
}