Sevice Computing: CLI 命令行實用程序開發實戰Agenda 及 Linux下安裝配置Cobra教程
CLI 命令行實用程序開發實戰Agenda 及 Linux下安裝配置Cobra教程)
1、概述
命令行實用程序並不是都象 cat、more、grep 是簡單命令。go 項目管理程序,類似 java 項目管理 maven、Nodejs 項目管理程序 npm、git 命令行客戶端、 docker 與 kubernetes 容器管理工具等等都是採用了較複雜的命令行。即一個實用程序同時支持多個子命令,每個子命令有各自獨立的參數,命令之間可能存在共享的代碼或邏輯,同時隨着產品的發展,這些命令可能發生功能變化、添加新命令等。因此,符合 OCP 原則 的設計是至關重要的編程需求。
2、安裝配置Cobra
cobra的安裝
首先使用命令 go get -v github.com/spf13/cobra/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
這是熟悉的錯誤,解決辦法是需要安裝golang的項目依賴test和sys。
首先cd到$GOPATH/src/golang.org/x
文件夾下,用 git clone
下載 sys 和 text 項目,命令如下:
cd $GOPATH/src/golang.org/x
git clone https://github.com/golang/sys
git clone https://github.com/golang/text
可以看到該目錄下出現了sys和text兩個文件夾如下:
然後再重新執行命令 go get -v github.com/spf13/cobra/cobra
下載cobra:
然後執行命令go install github.com/spf13/cobra/cobra
, 安裝後在 $GOBIN 下出現了 cobra 可執行程序。
輸入命令cobra help
也可以看到有幫助文檔的輸出,說明安裝成功。
cobra的初始化
在新建的項目文件夾下使用cobra init --pkg-name agenda
命令可以初始化一個新的項目
其中agenda是你自定義的項目名稱,可以自行修改的,成功後初始化的項目結構如下:
cobra添加命令
在項目文件夾下使用Cobra add
命令可以爲你的程序添加新的命令,在本次實驗中,我添加的兩條命令分別是register和login那麼這裏添加命令的語句就是:
Cobra add register
Cobra add login
可以看到在cmd文件夾下除了最開始的root.go還出現了我們新建的兩條命令的.go文件,然後在這些文件當中進行編寫開發就可以了。
3、開發實踐
需求描述
-
功能需求: 設計一組命令完成 agenda 的管理,要求包括2條命令:
- agenda register -uUserName –password pass –[email protected] -f phone
用戶註冊
- 註冊新用戶時,用戶需設置一個唯一的用戶名和一個密碼。另外,還需登記郵箱及電話信息。
- 如果註冊時提供的用戶名已由其他用戶使用,應反饋一個適當的出錯信息;成功註冊後,亦應反饋一個成功註冊的信息。
- agenda login -uUserName –password pass
用戶登錄
- 用戶使用用戶名和密碼登錄 Agenda 系統。
- 用戶名和密碼同時正確則登錄成功並反饋一個成功登錄的信息。否則,登錄失敗並反饋一個失敗登錄的信息。
-
持久化要求:
- 使用 json 存儲 User 和 Meeting 實體
- 當前用戶信息存儲在 curUser.txt 中
-
項目目錄
- cmd :存放命令實現代碼
- entity :存放 User 和 Meeting 對象讀寫與處理邏輯
- 其他目錄 : 自由添加
-
日誌服務
- 使用 log 包記錄命令執行情況
代碼分析
register.go
通過init函數獲取用戶輸入的命令行的不同參數,包括用戶名,密碼,郵箱,電話號碼四種string類型的變量。
rootCmd.AddCommand(registerCmd)
registerCmd.Flags().StringP("user", "u", "Anonymous", "help message for username")
registerCmd.Flags().StringP("password", "p", "123", "help message for password")
registerCmd.Flags().StringP("email", "e", "[email protected]", "help message for email")
registerCmd.Flags().StringP("phone", "f", "13611263068", "help message for phone")
讀取用戶信息後,由於我們讀取用戶信息使用到的是 json 的框架。
JSON (JavaScript對象表示法)是一種簡單的數據交換格式。在語法上,它類似於JavaScript的對象和列表。以文字爲基礎,具有自我描述性且易於讓人閱讀。儘管JSON是JavaScript的一個子集,但JSON是獨立於語言的文本格式,並且採用了類似於C語言家族的一些習慣。JSON與XML最大的不同在於XML是一個完整的標記語言,而JSON不是。JSON由於比XML更小、更快,更易解析,以及瀏覽器的內建快速解析支持,使得其更適用於網絡數據傳輸領域。
Go的JSON包中有如下函數:
// json.go
package main
import (
"encoding/json"
"fmt"
)
type Server struct {
ServerName string
ServerIP string
}
type Serverslice struct {
Servers []Server
}
func main() {
var s Serverslice
str := `{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},
{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}`
json.Unmarshal([]byte(str), &s)
fmt.Println(s)
fmt.Println(s.Servers[0].ServerIP)
}
利用go中的json包我們可以實現將結構體序列化,我們用函數marshal來encode JSON data ,將用戶輸入轉換成json支持的格式以完成對json文件的讀寫操作,具體關於go中json的用法可以參考這篇 博客。
Run 匿名回調函數中我們需要遍歷所有用戶的名字,檢查是否與當前的用戶信息衝突。若不衝突則可以註冊,返回註冊信息及註冊成功,否則則返回返回錯誤信息,然後將命令信息記錄在log文件中。以下爲run匿名回調函數:
Run: func(cmd *cobra.Command, args []string) {
//實例化
str := userinfo{
Name: "",
Password: "",
Email: "",
Phone: "",
}
username, _:=cmd.Flags().GetString("user")
password, _:=cmd.Flags().GetString("password")
email, _:=cmd.Flags().GetString("email")
phone, _:=cmd.Flags().GetString("phone")
fmt.Println("register name : "+username)
fmt.Println("password : "+password)
fmt.Println("email : "+email)
fmt.Println("phone : "+phone)
fmt.Println("Register success!")
str.Name=username
str.Password=password
str.Email=email
str.Phone=phone
filename:="./entity/log.log"
logfile, errr:=os.OpenFile(filename,os.O_RDWR|os.O_APPEND,7)
if(errr!=nil){
fmt.Println("openfile fail")
}
defer logfile.Close()
debuglog:=log.New(logfile,"",log.LstdFlags)
//判斷是否已有已註冊同名用戶來判斷註冊是否成功
if checkuser(username)==true {
fmt.Println("username exist, create account fail")
debuglog.Println("register: username "+username+" exist, create account fail")
} else {
debuglog.Println("register: username: "+username+" password: "+password+" email: "+email+" phone:"+phone);
savecuruser(str)
input := readinfo()
input.Id=append(input.Id,str)
data, _:= json.Marshal(input)
saveinfo(data)
}
},
}
在register.go文件中我們還實現了一些功能函數以完成上述邏輯框架,包括保存信息,讀取信息等等,用來讀取整個data.json文件當中的內容以及將用戶新的輸入信息保存到data.json當中。
讀取之後,我們利用checkuser來對全部的信息進行一個遍歷比對,來查詢是否用戶新註冊的用戶名已經存在。
func checkuser(username string) bool{
input := readinfo()
l :=len(input.Id)
for i:=0;i<l;i++ {
if(input.Id[i].Name==username){
return true
}
}
return false
}
完整代碼見GitHub。
login.go
login的代碼要簡單很多,以下爲run回調函數,這裏的核心其實就是用戶輸入的用戶名密碼是否存在且是否正確,調用了一個checkpasswd函數返回check值。
Run: func(cmd *cobra.Command, args []string) {
//fmt.Println("login called")
username, _:=cmd.Flags().GetString("username")
password, _:=cmd.Flags().GetString("password")
check := checkpasswd(username, password)
filename:="./entity/log.log"
logfile, errr:=os.OpenFile(filename,os.O_RDWR|os.O_APPEND,7)
if(errr!=nil){
fmt.Println("openfile fail")
}
defer logfile.Close()
debuglog:=log.New(logfile,"",log.LstdFlags)
if check==true {
fmt.Println("login success")
debuglog.Println("login: username: "+username+" login success")
} else {
fmt.Println("login fail")
debuglog.Println("login: username: "+username+" login fail")
}
},
主要就是進行兩個判斷:一個是判斷用戶是否存在,第二個是判斷密碼是否匹配,以此來進行錯誤反饋。
func checkpasswd(username string, password string) bool{
input := readinfo()
l :=len(input.Id)
for i:=0;i<l;i++ {
if(input.Id[i].Name==username && input.Id[i].Password==password){
return true
}
}
return false
}
root.go
root文件參考了這篇博客的內容,詳情請移步參考文檔:Golang: Cobra命令行參數庫的使用
代碼見GitHub。
運行結果測試
測試環境:centos
- register
成功註冊:
不成功註冊,用戶名重複:
- login
- log
- curUser
- data.json
我的Github代碼傳送門