前言

对于一名初学者来说,想要尽快熟悉 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. 基础的markdown用法

    首先推荐一款在线的markdown编辑器:Editor.md 一篇博客里面就是文字与图片的集合.markdown其实就是编辑文字,插入图片的工具.对于大多数人来说,学习几个标签就可以了. 标题 #** ...

  2. Visual C++线程同步技术剖析:临界区,时间,信号量,互斥量

    使线程同步 在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作.更多的情况是一些线程进行某些处理操作,而其他的线程必须对其处理结果进行了解.正常情况下对这种处理结果的了解应当在其 ...

  3. java简介&&变量

    Day01 简介 数据 1.数据大体分为两类:基本类型和引用类型 2.基本类型的数据分为四类八种,四类为整型,浮点,布尔,字符 3.Long类型的数据超过int范围要在之后面加个L,不加L是整型会进行 ...

  4. P3879 阅读理解

    都这么大了,you这些怎么能算生词呢,难道三年级以前就有人做蓝题了吗(是我不配) 我觉得这道题出难一点点的话,可以整行读入什么的(口嗨怪).先看题目,对于每个生词,输出他出现在了哪些文章(需要排序). ...

  5. tkinter的控件

    包含如下控件: Button  按钮控件 Canvas 画布控件,显示图形元素如线程或文本 Checkbutton 多选框控件,用于在程序中提供多项选择框 Entry 单行输入控件,用于显示简单的文本 ...

  6. Cache写策略(Cache一致性问题与骚操作)

    写命中 写直达(Write Through) 信息会被同时写到cache的块和主存中.这样做虽然比较慢,但缺少代价小,不需要把整个块都写回主存.也不会发生一致性问题. 对于写直达,多出来%10向主存写 ...

  7. css input checkbox复选框控件 样式美化的多种方案

    checkbox复选框可能是网站中常用的html元素,但大多数人并不满意它的默认样式,这篇文章就讲讲如何实现input checkbox复选框控件 样式美化效果. 资源网站大全 https://55w ...

  8. Java基础-网络编程1

    网络编程 Socket 基本概念 C/S结构 :全称为Client/Server结构,是指客户端和服务器结构.常见程序有QQ.迅雷等软件. B/S结构 :全称为Browser/Server结构,是指浏 ...

  9. vue 3.0 体验,vue 3.0新特性

    前言 昨天不是尤雨溪 不是刚在B站 直播玩了,分享了vue-next v3.0.0-beta.1 版本 哈哈, 不要太刺激哦 6大亮点 Performance:性能更比Vue 2.0强. Tree s ...

  10. Mysql 查找表中的多组前n大元素

    博客已搬家,更多内容查看https://liangyongrui.github.io/ Mysql 查找表中的多组前n大元素 如果时单组很简单,只需要排序后去前n个就行了,但是多组排序似乎就不是那么好 ...