前言

对于一名初学者来说,想要尽快熟悉 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. xxl-job搭建、部署、SpringBoot集成xxl-job

    一.搭建xxl-job 1.下载xxl-job代码 码云地址:https://gitee.com/xuxueli0323/xxl-job gitHub地址:https://github.com/xux ...

  2. Docker基本命令及工作原理

    第一个Docker容器     1.首先确保Docker运行正常:docker info

  3. MongoDB快速入门教程 (3.3)

    3.4.聚合 3.4.1.什么是聚合? MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果.有点类似sql语句中的 count(*) 例如上图 ...

  4. Redis系列之简介和Linux部署教程

    ##Redis介绍##Redis如今已经成为Web开发社区最火热的内存数据库之一,随着Web2.0的快速发展,再加上半结构数据比重加大,网站对高效性能的需求也越来越多.而且大型网站一般都有几百台或者更 ...

  5. 扯淡 Spring BeanDefinition

    相关文章 Spring 整体架构 编译Spring5.2.0源码 Spring-资源加载 Spring 容器的初始化 Spring-AliasRegistry Spring 获取单例流程(一) Spr ...

  6. git bash中提示 bash:node: command not found

    昨天小伙伴私信,git bash以及windows 的cmd命令行下均无法运行node npm. 究其原因是环境变量的问题.解决步骤: 1>在"此电脑"中右击,选择" ...

  7. NOI 2003 逃学的小孩 (树的直径)

    [NOI2003 逃学的小孩] 题目描述 Chris家的电话铃响起了,里面传出了Chris的老师焦急的声音:"喂,是Chris的家长吗?你们的孩子又没来上课,不想参加考试了吗?"一 ...

  8. 解决idea中“系统找不到指定路径”的问题

    有时在其他工具中运行正确的项目,在idea中运行会报路径找不到的错误. 该路径是相对路径的情况下,很有可能是因为idea中的工作空间没有选择正确而导致的.设置工作空间: 该目录没有配置到你运行的模块, ...

  9. Django---进阶7

    目录 图书管理的图书增删改查 choices参数(数据库字段设计常见) MTV与MVC模型 多对多三种创建方式 Ajax 小例子 作业 图书管理的图书增删改查 from django.shortcut ...

  10. mysql 时间自动更新问题

    万恶的根据当前时间戳更新,有时候会自动更新,有时候又不会. 通过navicat 创建datetime类型的字段时,会自动勾选上根据当前时间戳更新.当更新数据的时候勾上这个的字段时间就会更改为当前的时间 ...