Go语言核心36讲
郝林
《Go并发编程实战》作者,前轻松筹大数据负责人
立即订阅
24139 人已学习
课程目录
已完结 54 讲
0/4登录后,你可以任选4讲全文学习。
开篇词+学习路线 (3讲)
开篇词 | 跟着学,你也能成为Go语言高手
免费
预习篇 | 写给0基础入门的Go语言学习者
50 | 学习专栏的正确姿势
模块一:Go语言基础知识 (6讲)
01 | 工作区和GOPATH
02 | 命令源码文件
03 | 库源码文件
04 | 程序实体的那些事儿(上)
05 | 程序实体的那些事儿(中)
06 | 程序实体的那些事儿 (下)
模块二:Go语言进阶技术 (16讲)
07 | 数组和切片
08 | container包中的那些容器
09 | 字典的操作和约束
10 | 通道的基本操作
11 | 通道的高级玩法
12 | 使用函数的正确姿势
13 | 结构体及其方法的使用法门
14 | 接口类型的合理运用
15 | 关于指针的有限操作
16 | go语句及其执行规则(上)
17 | go语句及其执行规则(下)
18 | if语句、for语句和switch语句
19 | 错误处理(上)
20 | 错误处理 (下)
21 | panic函数、recover函数以及defer语句 (上)
22 | panic函数、recover函数以及defer语句(下)
模块三:Go语言实战与应用 (27讲)
23 | 测试的基本规则和流程 (上)
24 | 测试的基本规则和流程(下)
25 | 更多的测试手法
26 | sync.Mutex与sync.RWMutex
27 | 条件变量sync.Cond (上)
28 | 条件变量sync.Cond (下)
29 | 原子操作(上)
30 | 原子操作(下)
31 | sync.WaitGroup和sync.Once
32 | context.Context类型
33 | 临时对象池sync.Pool
34 | 并发安全字典sync.Map (上)
35 | 并发安全字典sync.Map (下)
36 | unicode与字符编码
37 | strings包与字符串操作
38 | bytes包与字节串操作(上)
39 | bytes包与字节串操作(下)
40 | io包中的接口和工具 (上)
41 | io包中的接口和工具 (下)
42 | bufio包中的数据类型 (上)
43 | bufio包中的数据类型(下)
44 | 使用os包中的API (上)
45 | 使用os包中的API (下)
46 | 访问网络服务
47 | 基于HTTP协议的网络服务
48 | 程序性能分析基础(上)
49 | 程序性能分析基础(下)
尾声与思考题答案 (2讲)
尾声 | 愿你披荆斩棘,所向无敌
新年彩蛋 | 完整版思考题答案
Go语言核心36讲
登录|注册

02 | 命令源码文件

郝林 2018-08-13
我们已经知道,环境变量 GOPATH 指向的是一个或多个工作区,每个工作区中都会有以代码包为基本组织形式的源码文件。
这里的源码文件又分为三种,即:命令源码文件、库源码文件和测试源码文件,它们都有着不同的用途和编写规则。( 我在“预习篇”的基础知识图介绍过这三种文件的基本情况。)
(长按保存大图查看)
今天,我们就沿着命令源码文件的知识点,展开更深层级的学习。
一旦开始学习用编程语言编写程序,我们就一定希望在编码的过程中及时地得到反馈,只有这样才能清楚对错。实际上,我们的有效学习和进步,都是通过不断地接受反馈和执行修正实现的。
对于 Go 语言学习者来说,你在学习阶段中,也一定会经常编写可以直接运行的程序。这样的程序肯定会涉及命令源码文件的编写,而且,命令源码文件也可以很方便地用go run命令启动。
那么,我今天的问题就是:命令源码文件的用途是什么,怎样编写它?
这里,我给出你一个参考的回答:命令源码文件是程序的运行入口,是每个可独立运行的程序必须拥有的。我们可以通过构建或安装,生成与其对应的可执行文件,后者一般会与该命令源码文件的直接父目录同名。
如果一个源码文件声明属于main包,并且包含一个无参数声明且无结果声明的main函数,那么它就是命令源码文件。 就像下面这段代码:
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Go语言核心36讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(49)

  • 姜雨生 置顶
    flag的讲解很棒,通过这个命令,我们就可以控制程序在不同环境的执行内容了,通过控制参数设置更多的内容!
    2019-02-28
    4
  • 咖啡色的羊驼
    看完本文,记住的两点:
    1.源码文件分为三种:命令,库,测试。
    2.编写命令源码文件的关键包: flag。

    回答下问题:
    1.命令源码文件支持的参数:
          int(int|int64|uint|uint64),
          float(float|float64)
          string,
          bool,
          duration(时间),
          var(自定义)
    2.关键就是使用flag.var(),关键点在于需要实现flag包的Value接口。


    2018-08-13
    74
  • Dragoonium
    我试着把参数增加到两个,然后试试运行结果
    func init() {
      flag.StringVar(&name, "name1", "ladies", "The greeting object 1")
      flag.StringVar(&name, "name2", "gentlemen", "The greeting object 2")
    }

    # go run test.go
    Hello gentlemen!
    和想像的一样,name2的默认值覆盖了name1的默认值

    # go run test.go -name1=Robert
    Hello Robert!
    和想像的略有不同,只指定了name1,没有指定name2,输出了name1的指定值,name2的默认值没有生效

    # go run test.go -name2=Jose
    Hello Jose!
    没毛病

    # go run test.go -name1=Robert -name2=Jose
    Hello Jose!
    没毛病

    # go run test.go -name2=Jose -name1=Robert
    Hello Robert!
    这有点奇怪了,输出的值是以参数的先后顺序为准的,而不是以flag.StringVar函数的顺序为准的
    2018-08-13
    2
    52
  • Abirdcfly
    解答一下Dragoonium同学的疑惑,在flag包的文档里第一个example里就有你提到的这种情况,注释已经说明白了。

    我不太精确的翻译一下:

    两个flag分享一个变量时可以用来做命令参数缩写,由于初始化顺序未定义,所以要确保两者使用同一默认值,并且他们必须在init()函数中设置
    2018-08-14
    1
    19
  • Tron
    go语言ide还是推荐goland
    2018-09-05
    15
  • javaadu
    喜欢这种重实践和编码的风格,便于上手
    2018-08-13
    9
  • wjq310
    demo3之后,要import os
    2018-08-15
    8
  • 何何何何何少侠
    1. 默认情况下,我们可以让命令源码文件接受哪些类型的参数值?
        答:前面讲过`flag`是专门用来处理命令行参数的包,所以我们只需要看`flag`这个包支持哪些数据结构就行了。结果如下:
        * int(int|int64|uint|uint64),
        * float(float|float64)
        * string,
        * bool,
        * time.duration(时间),
        * var(自定义,如FlagSet、Flag、实现Value接口等)
    2. 我们可以把自定义的数据类型作为参数值的类型吗?如果可以,怎样做?
        答:显然是可以的,从上面的回答来看就知道可以。实现方式可以参考源码中`flag.CommandLine.Var(value Value,name string,usage string)`的实现,核心在于自定义的数据类型需要实现`Value`接口,只要实现了`Value`接口,这个问题也就解决了。
    1. 目录结构
        * rexlin600(我的`GOPATH`)
            * pkg
            * bin
            * src
                * flagfunc
                    * custom_type.go
                    * tflag.go
                * main
                    * main.go
        
        2. 编写`main.go`和`tflag.go`以及自定义的`custom_type.go`
        `main.go`
        ```
        package main
        import (
            "flagfunc"
            "fmt"
        )
        // main method
        func main() {
            fmt.Println("==> hello,", flagfunc.CustomFlag)
        }
        ```
        
        `tflag.go`
        ```
        package flagfunc
        import (
            "flag"
            "fmt"
        )
        var CustomFlag customFlag
        // 测试自定义数据类型flag
        func TestCustomFlag() {
            fmt.Println("==> package flagfunc")
            // custom define Value
            flag.CommandLine.Var(&CustomFlag, "custom", "custom define test")
            // After all flags are defined, call flag.Parse() to parse the command line into the defined flags.
            flag.Parse()
        }
        ```
        
        `custom_type.go`
        ```
        package flagfunc
        import (
            "strings"
        )
        type customFlag string
        func newStringValue(val string, p *string) *customFlag {
            *p = val
            return (*customFlag)(p)
        }
        func (a *customFlag) Set(str string) error {
            str = strings.TrimSpace(str)
            *a = customFlag(str)
            return nil
        }
        func (a *customFlag) String() string { return "" }
        ```
        3. 测试:进入main.go所在路径,输入命令`go run main.go custom="{"\"rexlin600\"",18,"\"3072@qq.com\""}"`即可
    2018-12-08
    7
  • 云学
    init在main之前执行,go程序的执行顺序是否可以讲下
    2018-08-13
    6
  • alan
    感谢老师。感觉编码细节有些偏多了哈,希望多一些总结性和主观的内容。
    2018-08-13
    6
  • 吉祥
    undefined: os 怎么回事

    作者回复: 你好,请到GitHub上下载完整的源码文件。

    2018-08-13
    4
  • zhaopan
    1. 会出现冲突
    2. 导入包的几种方式
      2.1 常规方式
        import “your/lib”
        通过包名lib调用SayHello方法。lib.SayHello()
      2.2 别名方式
        import m “your/lib”
        这里的 m 是包名 lib 的别名,m.SyayHello() 。该方式合适的场景:
        1.包名过于复杂或者意思不明确。
        2.包名和其他包冲突。
          import mongo "mywebapp/libs/mongodb/db"
          import ydbgo "mywebapp/libs/yundb/db"
      2.3 省略包名
        import . “your/lib”
        这里的点.符号表示,对包 lib 的调用直接省略包名.
      2.4 初始化方式
        improt _ “your/lib”
        特殊符号“_” 仅仅会导致 lib 执行初始化工作,如初始化全局变量,调用 init 函数。


    2019-02-17
    3
  • 梦里追逐
    咱们用的都是哪个IDE?

    作者回复: 你好,我用的是goland,但是代码不会依赖于IDE的,只会依赖于Go语言本身。免费的编辑器推荐vs code。

    2018-08-15
    2
  • 成都福哥
    用自定义的cmdLine的时候,usage函数里的flag.PrintDefaults()应该相应的变成cmdLine.PrintDefaults()吧。
    2018-08-15
    2
  • 丸子说
    从flag.stringvar/flag.string到flag.commandline再到私有cmdline命令参数容器,循序渐进,由浅到深。
    2018-08-14
    2
  • 芒果
    关于变量以标准输入为准的问题,我个人认为init中的定义只是定义了解析规则,真正执行解析是flag.Parse()时开始,因此以标准输出为准。想想我们自己写的时候会怎么实现,先获得输入如:-name1=a,然后解析为key=name1和value=a,然后走一个if,else判断,如果key匹配则对其赋值。所以就很好解释了。个人感觉自己的理解还是比较靠谱的,虽然没有研究源码。欢迎大神们交流
    2018-08-14
    2
  • 松烽
    自定义参数,还可以自己通过字符串转对象的方式实现
    2018-08-14
    2
  • 飞吧蛐蛐
    问题1:通过flag库的提示,或者看flag包的用法,参数支持Bool/Duration/Float64/Int/Int64/Uint/Uint64,也支持Float32,猜测考虑到精度问题,flag没有支持float32。
    问题2:参数值的类型可以是自定义的数据类型,使用实现flag包里的Value接口,然后使用flag.Var()实现。(flag源码里有提示,Value is the interface to the dynamic value stored in a flag)

    PS:看一下flag包的用法和源码就能解答这两个问题了。
    2018-10-12
    1
  • 属鱼
    给VS Code的用户一个脚本,用于安装VS Code的Go扩展所需要的。
    VS code的go插件需要的扩展组件有的需要翻墙,安装不了,
    Linux
    https://github.com/cloudfstrife/note/blob/develop/golang/install_vs_code_golang_ext_tools.sh

    windows
    https://github.com/cloudfstrife/note/blob/develop/golang/install_vs_code_golang_ext_tools.ps1
    这个是powershell脚本,在win7 powershell 5测试通过。

    执行powershell 需要 修改策略,详见:
    http://www.cnblogs.com/Jim-william/p/5492507.html

    ## 注意shell中的说明,GOPATH目前只支持一个目录。
    2018-09-12
    1
  • mayunian
    老师,今天试了一下类型转换。
    为什么转换var x uint = uint(-1) 的时候会报错?
    而var y int = -1
    var x uint = uint(y)就不会报错呢?

    作者回复: -1是负数,编译器看出来了,帮你挑出来。y是int类型的变量,编译器不知道里面存的是不是负数,没法帮你挑出来。转换会成功结果会不正确。

    2018-08-30
    1
收起评论
49
返回
顶部