前言

对于一名初学者来说,想要尽快熟悉 Go 语言特性,所以以操作式的学习方法为主,比如编写一个简单的数学计算器,读取命令行参数,进行数学运算。

本文讲述使用三种方式讲述 Go 语言如何接受命令行参数,并完成一个简单的数学计算,为演示方便,最后的命令行结果大概是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 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

1
2
3
4
5
6
7
import sys
 
args = sys.argv
 
# args 是一个列表
# 第一个值表示的是 文件名
# 除第一个之外,其他的值是接受的参数

Shell

1
2
3
4
5
6
7
8
9
10
11
12
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 获取命令行参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
os.Args
 
# 为接受的参数,是一个切片
 
strconv.Atoi
 
# 将字符串数值转换为整型
 
strconv.Itoa
 
# 将整型转换为字符串
 
strconv.ParseFloat
 
# 将字符串数值转换为浮点型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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
}

最后的效果大概是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
./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 读取参数更方便。可以自定义传入的参数的类型:比如字符串,整型,浮点型,默认参数设置等

基本的使用方法如下:

1
2
3
var operate string
 
flag.StringVar(&operate,"o", "add", "operation for calc")

# 解释

绑定 operate 变量, name="o", value="add" , usage="operation for calc"

也可以这样定义为指针变量

1
var operate := flag.String("o", "add", "operation for calc")

同时还可以自定义 flag 类型

所有变量注册之后,调用 flag.Parse() 来解析命令行参数, 如果是绑定变量的方式,直接使用变量进行操作,
如果使用指针变量型,需要 *operate 这样使用。

flag.Args() 表示接收的所有命令行参数集, 也是一个切片

1
2
3
for index, value := range flag.Args {
 fmt.Println(index, value)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
}

最后的结果效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
./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 是一款业界比较流行的命令行框架。

所以你首先需要安装:

1
go get github.com/urfave/cli
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 一个简单的示例如下:
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 功能,对参数进行设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 参数进行判断,执行的是那种运算,然后编写相应的运算操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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)
}

调用这个函数的最终效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
./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 操作如下:

1
2
3
4
5
6
7
8
9
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 如下:

返回的结果是一个Json 格式的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
{
 "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 数据解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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() {
  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
}

最终的效果大概如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
./weather -c 上海
 
城市: 上海
湿度: 80%
空气质量: 轻度污染
温馨提示: 儿童、老年人及心脏、呼吸系统疾病患者人群应减少长时间或高强度户外锻炼
 
 
================================
./weaather -c 上海 -d 昨天
 
城市: 上海
日期: 28日星期二
温度: 低温 12.0℃ 高温 19.0℃
风量: 西南风 <3级
天气: 小雨
温馨提示: 雾蒙蒙的雨天,最喜欢一个人听音乐

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

Go 中读取命令参数的几种方法总结的更多相关文章

  1. php中读取文件内容的几种方法。(file_get_contents:将文件内容读入一个字符串)

    php中读取文件内容的几种方法.(file_get_contents:将文件内容读入一个字符串) 一.总结 php中读取文件内容的几种方法(file_get_contents:将文件内容读入一个字符串 ...

  2. MFC中获取命令行参数的几种方法

    在MFC程序中,可以用以下几种方法来获取命令行参数. 为方便说明,我们假设执行了命令:C:\test\app.exe -1 -2 方法一 ::GetCommandLine(); 将获取到 " ...

  3. php中读取文件内容的几种方法

    1.fread string fread ( int $handle , int $length ) fread() 从 handle 指向的文件中读取最多 length 个字节.该函数在读取完最多 ...

  4. 【转】flash air中读取本地文件的三种方法

    actionscript中读取本地文件操作有两种代码如下 1.使用File和FileStream两个类,FileStream负责读取数据的所以操作:(同步操作) var stream:FileStre ...

  5. Oracle中spool命令实现的两种方法比较

    ---恢复内容开始--- 要输出符合要求格式的数据文件只需在select时用字符连接来规范格式.比如有如下表 SQL>; select id,username,password from myu ...

  6. linux中touch命令参数修改文件的时间戳(转)

    linux中touch命令参数不常用,一般在使用make的时候可能会用到,用来修改文件时间戳,或者新建一个不存在的文件,以下是linux中touch命令参数的使用方法: touch [-acm][-r ...

  7. linux中touch命令参数修改文件的时间戳(转载)

    转自:http://os.51cto.com/art/200908/144237.htm linux中touch命令参数不常用,一般在使用make的时候可能会用到,用来修改文件时间戳,或者新建一个不存 ...

  8. Linux中mpstat命令参数详解

    Linux中mpstat命令参数详解 mpstat 是 Multiprocessor Statistics的缩写,是实时系统监控工具.其报告与CPU的一些统计信息,这些信息存放在 /proc/stat ...

  9. Python中函数传递参数有四种形式

    Python中函数传递参数有四种形式 fun1(a,b,c) fun2(a=1,b=2,c=3) fun3(*args) fun4(**kargs) 四种中最常见是前两种,基本上一般点的教程都会涉及, ...

随机推荐

  1. vue全家桶(3.2)

    4.5.创建实例 可以创建一个自定义实例应用于多个请求接口 <template> <div class="page"> </div> </ ...

  2. 只需几行 JavaScript 代码,网页瞬间有气质了!

    最近在网上闲逛,发现一个特别好玩的 JavaScript 库,叫 RoughNotation.干嘛用的呢?就是在网页上给文字加标注,比如下划线.方框.高亮文字背景等,不过是手写风格的!截图给大家感受下 ...

  3. IdentityServer4 4.x版本 配置Scope的正确姿势

    前言 IdentityServer4 是为ASP.NET Core系列量身打造的一款基于 OpenID Connect 和 OAuth 2.0 认证的框架 IdentityServer4官方文档:ht ...

  4. Mybatis 报错

    Mybatis 报错 builder.BuilderException: Error parsing SQL Mapper Configuration Caused by: org.apache.ib ...

  5. css hack原理及常用hack

    原理:利用不同浏览器对CSS的支持和解析结果不一样编写针对特定浏览器样式.常见的hack有1)属性hack.2)选择器hack.3)IE条件注释 IE条件注释:适用于[IE5, IE9]常见格式如下 ...

  6. AcWing 1208. 翻硬币

    AcWing 1208. 翻硬币 原题链接 小明正在玩一个"翻硬币"的游戏. 桌上放着排成一排的若干硬币.我们用 * 表示正面,用 o 表示反面(是小写字母,不是零). 比如,可能 ...

  7. bzoj3442学习小组

    bzoj3442学习小组 题意: 共有n个学生,m个学习小组,每个学生只愿意参加其中的一些学习小组,且一个学生最多参加k个学习小组.每个学生参加学习小组财务处都收一定的手续费,不同的学习小组有不同的手 ...

  8. Java数组最大值和最小值

    package com.mybatis.demo.exercise; /** * @author yinsenxin */ public class ArrayMaxAndMin { public s ...

  9. 面试软件测试工程师——盘点HR的那些黑话

    当疫情过后,应该有很多测试实习生寻找测试岗或者已从业测试岗的群体进行跳槽:最近也收到很多测试新生的咨询,在这里简单分享一下!老铁们走起!今天在这里就简单做跟大家聊一聊面试过程中你与面试官/HR聊天过程 ...

  10. Ethical Hacking - NETWORK PENETRATION TESTING(10)

    WPA Craking WPA was designed to address the issues in WEP and provide better encryption. The main is ...