前言
對于一名初學(xué)者來說,想要盡快熟悉 Go 語言特性,所以以操作式的學(xué)習(xí)方法為主,比如編寫一個簡單的數(shù)學(xué)計算器,讀取命令行參數(shù),進(jìn)行數(shù)學(xué)運(yùn)算。
本文講述使用三種方式講述 Go 語言如何接受命令行參數(shù),并完成一個簡單的數(shù)學(xué)計算,為演示方便,最后的命令行結(jié)果大概是這樣的:
# input
./calc add 1 2
# output
3
# input
./calc sub 1 2
# out
-1
# input
./calc mul 10 20
# out
200
使用的三種方式是:
- 內(nèi)置 os 包讀取命令參數(shù)
- 內(nèi)置 flag 包讀取命令參數(shù)
- cli 框架讀取命令參數(shù)
0. 已有歷史經(jīng)驗
如果你熟悉 Python 、Shell 腳本,你可以比較下:
Python
import sys
args = sys.argv
# args 是一個列表
# 第一個值表示的是 文件名
# 除第一個之外,其他的值是接受的參數(shù)
Shell
if [ $# -ne 2 ]; then
echo "Usage: $0 param1 pram2"
exit 1
fi
name=$1
age=$2
echo $name
echo $age
# `$0` 表示文件名
# `$1` 表示第一個參數(shù)
# `$2` 表示第二個參數(shù)
能看出一些共性,接收參數(shù),一般解析出來都是一個數(shù)組(列表、切片), 第一個元素表示的是文件名,剩余的參數(shù)表示接收的參數(shù)。
好,那么為了實現(xiàn) “簡單數(shù)學(xué)計算” 這個功能,讀取命令行參數(shù):比如 ./calc add 1 2
除文件名之外的第一個元素:解析為 進(jìn)行數(shù)學(xué)運(yùn)算的 操作,比如: add、sub、mul、sqrt
其余參數(shù)表示:進(jìn)行操作的數(shù)值
注意:命令行讀取的參數(shù)一般為字符串,進(jìn)行數(shù)值計算需要進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換
大概思路就是這樣。
1. OS 獲取命令行參數(shù)
os.Args
# 為接受的參數(shù),是一個切片
strconv.Atoi
# 將字符串?dāng)?shù)值轉(zhuǎn)換為整型
strconv.Itoa
# 將整型轉(zhuǎn)換為字符串
strconv.ParseFloat
# 將字符串?dāng)?shù)值轉(zhuǎn)換為浮點(diǎn)型
var help = func () {
fmt.Println("Usage for calc tool.")
fmt.Println("====================================================")
fmt.Println("add 1 2, return 3")
fmt.Println("sub 1 2, return -1")
fmt.Println("mul 1 2, return 2")
fmt.Println("sqrt 2, return 1.4142135623730951")
}
func CalcByOs() error {
args := os.Args
if len(args) 3 || args == nil {
help()
return nil
}
operate := args[1]
switch operate {
case "add":{
rt := 0
number_one, err1 := strconv.Atoi(args[2])
number_two, err2 := strconv.Atoi(args[3])
if err1 == nil err2 == nil {
rt = number_one + number_two
fmt.Println("Result ", rt)
}
}
case "sub":
{
rt := 0
number_one, err1 := strconv.Atoi(args[2])
number_two, err2 := strconv.Atoi(args[3])
if err1 == nil err2 == nil {
rt += number_one - number_two
fmt.Println("Result ", rt)
}
}
case "mul":
{
rt := 1
number_one, err1 := strconv.Atoi(args[2])
number_two, err2 := strconv.Atoi(args[3])
if err1 == nil err2 == nil {
rt = number_one * number_two
fmt.Println("Result ", rt)
}
}
case "sqrt":
{
rt := float64(0)
if len(args) != 3 {
fmt.Println("Usage: sqrt 2, return 1.4142135623730951")
return nil
}
number_one, err := strconv.ParseFloat(args[2], 64)
if err == nil {
rt = math.Sqrt(number_one)
fmt.Println("Result ", rt)
}
}
default:
help()
}
return nil
}
最后的效果大概是:
./calc add 1 2
Result 3
====================
./calc sub 1 2
Result -1
====================
./calc mul 10 20
Result 200
===================
./calc sqrt 2
Result 1.4142135623730951
2. flag 獲取命令行參數(shù)
flag 包比 os 讀取參數(shù)更方便??梢宰远x傳入的參數(shù)的類型:比如字符串,整型,浮點(diǎn)型,默認(rèn)參數(shù)設(shè)置等
基本的使用方法如下:
var operate string
flag.StringVar(operate,"o", "add", "operation for calc")
# 解釋
綁定 operate 變量, name="o", value="add" , usage="operation for calc"
也可以這樣定義為指針變量
var operate := flag.String("o", "add", "operation for calc")
同時還可以自定義 flag 類型
所有變量注冊之后,調(diào)用 flag.Parse() 來解析命令行參數(shù), 如果是綁定變量的方式,直接使用變量進(jìn)行操作,
如果使用指針變量型,需要 *operate 這樣使用。
flag.Args() 表示接收的所有命令行參數(shù)集, 也是一個切片
for index, value := range flag.Args {
fmt.Println(index, value)
}
func CalcByFlag() error {
var operation string
var numberone float64
var numbertwo float64
flag.StringVar(operation, "o", "add", "operation for this tool")
flag.Float64Var(numberone, "n1", 0, "The first number")
flag.Float64Var(numbertwo, "n2", 0, "The second number")
flag.Parse()
fmt.Println(numberone, numbertwo)
if operation == "add" {
rt := numberone + numbertwo
fmt.Println("Result ", rt)
} else if operation == "sub" {
rt := numberone - numbertwo
fmt.Println("Result ", rt)
} else if operation == "mul" {
rt := numberone * numbertwo
fmt.Println("Result ", rt)
} else if operation == "sqrt" {
rt := math.Sqrt(numberone)
fmt.Println("Result ", rt)
} else {
help()
}
return nil
}
最后的結(jié)果效果如下:
./calc -o add -n1 1 -n2 2
Result 3
=============================
./calc -o sub -n1 2 -n2 3
Result -1
============================
./calc -o mul -n1 10 -n2 20
Result 200
===========================
./calc -o sqrt -n1 2
Result 1.4142135623730951
3. CLI 框架
cli 是一款業(yè)界比較流行的命令行框架。
所以你首先需要安裝:
go get github.com/urfave/cli
# 一個簡單的示例如下:
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "boom"
app.Usage = "make an explosive entrance"
app.Action = func(c *cli.Context) error {
fmt.Println("boom! I say!")
return nil
}
app.Run(os.Args)
}
好,為實現(xiàn) “簡單數(shù)學(xué)計算” 的功能,我們應(yīng)該怎么實現(xiàn)呢?
主要是 使用 框架中的 Flag 功能,對參數(shù)進(jìn)行設(shè)置
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "operation, o",
Value: "add",
Usage: "calc operation",
},
cli.Float64Flag{
Name: "numberone, n1",
Value: 0,
Usage: "number one for operation",
},
cli.Float64Flag{
Name: "numbertwo, n2",
Value: 0,
Usage: "number two for operation",
},
}
能看出,我們使用了三個參數(shù):operation、numberone、numbertwo
同時定義了參數(shù)的類型,默認(rèn)值,以及別名(縮寫)
那么在這個框架中如何實現(xiàn)參數(shù)的操作呢:主要是重寫app.Action 方法
app.Action = func(c *cli.Context) error {
operation := c.String("operation")
numberone := c.Float64("numberone")
numbertwo := c.Float64("numbertwo")
//fmt.Println(operation, numberone, numbertwo)
if operation == "add" {
rt := numberone + numbertwo
fmt.Println("Result ", rt)
} else if operation == "sub" {
rt := numberone - numbertwo
fmt.Println("Result ", rt)
} else if operation == "mul" {
rt := numberone * numbertwo
fmt.Println("Result ", rt)
} else if operation == "sqrt" {
rt := math.Sqrt(numberone)
fmt.Println("Result ", rt)
} else {
help()
}
return nil
}
# 對 operation 參數(shù)進(jìn)行判斷,執(zhí)行的是那種運(yùn)算,然后編寫相應(yīng)的運(yùn)算操作
func CalcByCli(){
app := cli.NewApp()
app.Name = "calc with go"
app.Usage = "calc tool operate by go"
app.Version = "0.1.0"
app.Flags = [] cli.Flag {
cli.StringFlag{
Name: "operation, o",
Value: "add",
Usage: "calc operation",
},
cli.Float64Flag{
Name: "numberone, n1",
Value: 0,
Usage: "number one for operation",
},
cli.Float64Flag{
Name: "numbertwo, n2",
Value: 0,
Usage: "number two for operation",
},
}
app.Action = func(c *cli.Context) error {
operation := c.String("operation")
numberone := c.Float64("numberone")
numbertwo := c.Float64("numbertwo")
//fmt.Println(operation, numberone, numbertwo)
if operation == "add" {
rt := numberone + numbertwo
fmt.Println("Result ", rt)
} else if operation == "sub" {
rt := numberone - numbertwo
fmt.Println("Result ", rt)
} else if operation == "mul" {
rt := numberone * numbertwo
fmt.Println("Result ", rt)
} else if operation == "sqrt" {
rt := math.Sqrt(numberone)
fmt.Println("Result ", rt)
} else {
help()
}
return nil
}
app.Run(os.Args)
}
調(diào)用這個函數(shù)的最終效果如下:
./calc -o add --n1 12 --n2 12
Result 24
===================================
./calc -o sub --n1 100 --n2 200
Result -100
===================================
./calc -o mul --n1 10 --n2 20
Result 200
===================================
./calc -o sqrt --n1 2
Result 1.4142135623730951
4 其他
知道如何讀取命令行參數(shù),就可以實現(xiàn)一些更有意思的事。
比如網(wǎng)上有許多免費(fèi)的 API 接口,比如查詢天氣,查詢農(nóng)歷的API 接口。
還有一些查詢接口,比如有道云翻譯接口,你可以實現(xiàn)翻譯的功能。
或者扇貝的接口,實現(xiàn)查詢單詞的功能。
再比如一些音樂接口,實現(xiàn)音樂信息查詢。
不一一列了。
下面實現(xiàn)一個調(diào)用免費(fèi)的查詢天氣的接口實現(xiàn)命令行查詢天氣。
GO 如何進(jìn)行 HTTP 訪問?內(nèi)置的 net/http 可以實現(xiàn)
一個簡易的GET 操作如下:
func Requests(url string) (string, error) {
response, err := http.Get(url)
if err != nil {
return "", err
}
defer response.Body.Close()
body, _ := ioutil.ReadAll(response.Body)
return string(body), nil
}
免費(fèi)的 API URL 如下:
http://www.sojson.com/open/api/weather/json.shtml?city=北京
返回的結(jié)果是一個Json 格式的數(shù)據(jù)
{
"status": 200,
"data": {
"wendu": "29",
"ganmao": "各項氣象條件適宜,發(fā)生感冒機(jī)率較低。但請避免長期處于空調(diào)房間中,以防感冒。",
"forecast": [
{
"fengxiang": "南風(fēng)",
"fengli": "3-4級",
"high": "高溫 32℃",
"type": "多云",
"low": "低溫 17℃",
"date": "16日星期二"
},
{
"fengxiang": "南風(fēng)",
"fengli": "微風(fēng)級",
"high": "高溫 34℃",
"type": "晴",
"low": "低溫 19℃",
"date": "17日星期三"
},
{
"fengxiang": "南風(fēng)",
"fengli": "微風(fēng)級",
"high": "高溫 35℃",
"type": "晴",
"low": "低溫 22℃",
"date": "18日星期四"
},
{
"fengxiang": "南風(fēng)",
"fengli": "微風(fēng)級",
"high": "高溫 35℃",
"type": "多云",
"low": "低溫 22℃",
"date": "19日星期五"
},
{
"fengxiang": "南風(fēng)",
"fengli": "3-4級",
"high": "高溫 34℃",
"type": "晴",
"low": "低溫 21℃",
"date": "20日星期六"
}
],
"yesterday": {
"fl": "微風(fēng)",
"fx": "南風(fēng)",
"high": "高溫 28℃",
"type": "晴",
"low": "低溫 15℃",
"date": "15日星期一"
},
"aqi": "72",
"city": "北京"
},
"message": "OK"
}
所以我們的任務(wù)就是傳入 “城市” 的名稱,再對返回的 Json 數(shù)據(jù)解析。
package main
import (
"fmt"
"os"
"encoding/json"
"github.com/urfave/cli"
"net/http"
"io/ioutil"
//"github.com/modood/table"
)
type Response struct {
Status int `json:"status"`
CityName string `json:"city"`
Data Data `json:"data"`
Date string `json:"date"`
Message string `json:"message"`
Count int `json:"count"`
}
type Data struct {
ShiDu string `json:"shidu"`
Quality string `json:"quality"`
Ganmao string `json:"ganmao"`
Yesterday Day `json:"yesterday"`
Forecast []Day `json:"forecast"`
}
type Day struct {
Date string `json:"date"`
Sunrise string `json:"sunrise"`
High string `json:"high"`
Low string `json:"low"`
Sunset string `json:"sunset"`
Aqi float32 `json:"aqi"`
Fx string `json:"fx"`
Fl string `json:"fl"`
Type string `json:"type"`
Notice string `json:"notice"`
}
func main() {
const apiURL = "http://www.sojson.com/open/api/weather/json.shtml?city="
app := cli.NewApp()
app.Name = "weather-cli"
app.Usage = "天氣預(yù)報小程序"
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "city, c",
Value: "上海",
Usage: "城市中文名",
},
cli.StringFlag{
Name: "day, d",
Value: "今天",
Usage: "可選: 今天, 昨天, 預(yù)測",
},
cli.StringFlag{
Name: "Author, r",
Value: "xiewei",
Usage: "Author name",
},
}
app.Action = func(c *cli.Context) error {
city := c.String("city")
day := c.String("day")
var body, err = Requests(apiURL + city)
if err != nil {
fmt.Printf("err was %v", err)
return nil
}
var r Response
err = json.Unmarshal([]byte(body), r)
if err != nil {
fmt.Printf("\nError message: %v", err)
return nil
}
if r.Status != 200 {
fmt.Printf("獲取天氣API出現(xiàn)錯誤, %s", r.Message)
return nil
}
Print(day, r)
return nil
}
app.Run(os.Args)
}
func Print(day string, r Response) {
fmt.Println("城市:", r.CityName)
if day == "今天" {
fmt.Println("濕度:", r.Data.ShiDu)
fmt.Println("空氣質(zhì)量:", r.Data.Quality)
fmt.Println("溫馨提示:", r.Data.Ganmao)
} else if day == "昨天" {
fmt.Println("日期:", r.Data.Yesterday.Date)
fmt.Println("溫度:", r.Data.Yesterday.Low, r.Data.Yesterday.High)
fmt.Println("風(fēng)量:", r.Data.Yesterday.Fx, r.Data.Yesterday.Fl)
fmt.Println("天氣:", r.Data.Yesterday.Type)
fmt.Println("溫馨提示:", r.Data.Yesterday.Notice)
} else if day == "預(yù)測" {
fmt.Println("====================================")
for _, item := range r.Data.Forecast {
fmt.Println("日期:", item.Date)
fmt.Println("溫度:", item.Low, item.High)
fmt.Println("風(fēng)量:", item.Fx, item.Fl)
fmt.Println("天氣:", item.Type)
fmt.Println("溫馨提示:", item.Notice)
fmt.Println("====================================")
}
} else {
fmt.Println("...")
}
}
func Requests(url string) (string, error) {
response, err := http.Get(url)
if err != nil {
return "", err
}
defer response.Body.Close()
body, _ := ioutil.ReadAll(response.Body)
return string(body), nil
}
最終的效果大概如下:
./weather -c 上海
城市: 上海
濕度: 80%
空氣質(zhì)量: 輕度污染
溫馨提示: 兒童、老年人及心臟、呼吸系統(tǒng)疾病患者人群應(yīng)減少長時間或高強(qiáng)度戶外鍛煉
================================
./weaather -c 上海 -d 昨天
城市: 上海
日期: 28日星期二
溫度: 低溫 12.0℃ 高溫 19.0℃
風(fēng)量: 西南風(fēng) 3級
天氣: 小雨
溫馨提示: 霧蒙蒙的雨天,最喜歡一個人聽音樂
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
您可能感興趣的文章:- Go語言中使用flag包對命令行進(jìn)行參數(shù)解析的方法
- Go語言中一些不常見的命令參數(shù)詳解