前言
對于一名初學者來說,想要盡快熟悉 Go 語言特性,所以以操作式的學習方法為主,比如編寫一個簡單的數學計算器,讀取命令行參數,進行數學運算。
本文講述使用三種方式講述 Go 語言如何接受命令行參數,并完成一個簡單的數學計算,為演示方便,最后的命令行結果大概是這樣的:
# input
./calc add 1 2
# output
3
# input
./calc sub 1 2
# out
-1
# input
./calc mul 10 20
# out
200
使用的三種方式是:
- 內置 os 包讀取命令參數
- 內置 flag 包讀取命令參數
- cli 框架讀取命令參數
0. 已有歷史經驗
如果你熟悉 Python 、Shell 腳本,你可以比較下:
Python
import sys
args = sys.argv
# args 是一個列表
# 第一個值表示的是 文件名
# 除第一個之外,其他的值是接受的參數
Shell
if [ $# -ne 2 ]; then
echo "Usage: $0 param1 pram2"
exit 1
fi
name=$1
age=$2
echo $name
echo $age
# `$0` 表示文件名
# `$1` 表示第一個參數
# `$2` 表示第二個參數
能看出一些共性,接收參數,一般解析出來都是一個數組(列表、切片), 第一個元素表示的是文件名,剩余的參數表示接收的參數。
好,那么為了實現 “簡單數學計算” 這個功能,讀取命令行參數:比如 ./calc add 1 2
除文件名之外的第一個元素:解析為 進行數學運算的 操作,比如: add、sub、mul、sqrt
其余參數表示:進行操作的數值
注意:命令行讀取的參數一般為字符串,進行數值計算需要進行數據類型轉換
大概思路就是這樣。
1. OS 獲取命令行參數
os.Args
# 為接受的參數,是一個切片
strconv.Atoi
# 將字符串數值轉換為整型
strconv.Itoa
# 將整型轉換為字符串
strconv.ParseFloat
# 將字符串數值轉換為浮點型
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 獲取命令行參數
flag 包比 os 讀取參數更方便。可以自定義傳入的參數的類型:比如字符串,整型,浮點型,默認參數設置等
基本的使用方法如下:
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 類型
所有變量注冊之后,調用 flag.Parse() 來解析命令行參數, 如果是綁定變量的方式,直接使用變量進行操作,
如果使用指針變量型,需要 *operate 這樣使用。
flag.Args() 表示接收的所有命令行參數集, 也是一個切片
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
}
最后的結果效果如下:
./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 是一款業界比較流行的命令行框架。
所以你首先需要安裝:
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)
}
好,為實現 “簡單數學計算” 的功能,我們應該怎么實現呢?
主要是 使用 框架中的 Flag 功能,對參數進行設置
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",
},
}
能看出,我們使用了三個參數:operation、numberone、numbertwo
同時定義了參數的類型,默認值,以及別名(縮寫)
那么在這個框架中如何實現參數的操作呢:主要是重寫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 參數進行判斷,執行的是那種運算,然后編寫相應的運算操作
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)
}
調用這個函數的最終效果如下:
./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 其他
知道如何讀取命令行參數,就可以實現一些更有意思的事。
比如網上有許多免費的 API 接口,比如查詢天氣,查詢農歷的API 接口。
還有一些查詢接口,比如有道云翻譯接口,你可以實現翻譯的功能。
或者扇貝的接口,實現查詢單詞的功能。
再比如一些音樂接口,實現音樂信息查詢。
不一一列了。
下面實現一個調用免費的查詢天氣的接口實現命令行查詢天氣。
GO 如何進行 HTTP 訪問?內置的 net/http 可以實現
一個簡易的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
}
免費的 API URL 如下:
http://www.sojson.com/open/api/weather/json.shtml?city=北京
返回的結果是一個Json 格式的數據
{
"status": 200,
"data": {
"wendu": "29",
"ganmao": "各項氣象條件適宜,發生感冒機率較低。但請避免長期處于空調房間中,以防感冒。",
"forecast": [
{
"fengxiang": "南風",
"fengli": "3-4級",
"high": "高溫 32℃",
"type": "多云",
"low": "低溫 17℃",
"date": "16日星期二"
},
{
"fengxiang": "南風",
"fengli": "微風級",
"high": "高溫 34℃",
"type": "晴",
"low": "低溫 19℃",
"date": "17日星期三"
},
{
"fengxiang": "南風",
"fengli": "微風級",
"high": "高溫 35℃",
"type": "晴",
"low": "低溫 22℃",
"date": "18日星期四"
},
{
"fengxiang": "南風",
"fengli": "微風級",
"high": "高溫 35℃",
"type": "多云",
"low": "低溫 22℃",
"date": "19日星期五"
},
{
"fengxiang": "南風",
"fengli": "3-4級",
"high": "高溫 34℃",
"type": "晴",
"low": "低溫 21℃",
"date": "20日星期六"
}
],
"yesterday": {
"fl": "微風",
"fx": "南風",
"high": "高溫 28℃",
"type": "晴",
"low": "低溫 15℃",
"date": "15日星期一"
},
"aqi": "72",
"city": "北京"
},
"message": "OK"
}
所以我們的任務就是傳入 “城市” 的名稱,再對返回的 Json 數據解析。
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 = "天氣預報小程序"
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "city, c",
Value: "上海",
Usage: "城市中文名",
},
cli.StringFlag{
Name: "day, d",
Value: "今天",
Usage: "可選: 今天, 昨天, 預測",
},
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出現錯誤, %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("空氣質量:", 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("風量:", r.Data.Yesterday.Fx, r.Data.Yesterday.Fl)
fmt.Println("天氣:", r.Data.Yesterday.Type)
fmt.Println("溫馨提示:", r.Data.Yesterday.Notice)
} else if day == "預測" {
fmt.Println("====================================")
for _, item := range r.Data.Forecast {
fmt.Println("日期:", item.Date)
fmt.Println("溫度:", item.Low, item.High)
fmt.Println("風量:", 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%
空氣質量: 輕度污染
溫馨提示: 兒童、老年人及心臟、呼吸系統疾病患者人群應減少長時間或高強度戶外鍛煉
================================
./weaather -c 上海 -d 昨天
城市: 上海
日期: 28日星期二
溫度: 低溫 12.0℃ 高溫 19.0℃
風量: 西南風 3級
天氣: 小雨
溫馨提示: 霧蒙蒙的雨天,最喜歡一個人聽音樂
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
您可能感興趣的文章:- Go語言中使用flag包對命令行進行參數解析的方法
- Go語言中一些不常見的命令參數詳解