Go基础系列:构建go程序
hello world
从一个简单的程序开始解释,将下面的内容放进test.go文件中,路径随意:
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello World")
}
Go通过包的方式管理程序,每个Go源代码文件都必须声明自己所在的包,正如上面的package main
声明自己所在的包是main包。
每个程序都必须有一个main包,main包作为整个程序的编译入口包,main包中的main()函数作为程序的执行入口。
import关键字用来导入其它包,导入某个包之后就能在当前文件中使用这个包中的函数,例如上面的main包导入fmt包后,可以使用fmt包中的函数Println()。
然后可以使用go的build工具编译这个test.go文件:
$ go build test.go
编译后,将在当前路径下生成一个可执行二进制文件:Windows下生成的是test.exe文件,Unix下生成的是test文件。既然是可执行文件,当然可以直接执行:
$ ./test
将输出"Hello World"。
也可以直接通过go的run工具将编译和运行两个步骤合二为一:
$ go run test.go
Hello World
go run
不会生成可执行的二进制文件,它实际上是将编译得到的文件放进一个临时目录,然后执行,执行完后自动清理临时目录。
关于包和go文件
每个go代码文件只能且必须使用package语句声明一个包,也就是说一个文件中不能包含多个包。
Go中有两种类型的包,或者说有两种类型的文件:
- 编译后成为可执行文件的包,也就是main包编译后的得到的文件
- 编译后成为共享库的包,只要go程序文件中声明的不是main包,就是库文件
注意:
在go的官方文档中将go的二进制可执行程序称为命令,有时候还会将go的源代码文件称为命令的源文件。可执行程序和包相反,包一般是作为"库"文件存在,用于导入而非用于执行
共享库中包含一些函数,这些函数比较通用,所以放进共享库方便函数复用。例如fmt包中的Println函数,到处都在使用这个函数,且因为fmt包是标准库(Standary library),无论是谁都可以去使用这个包。
有两种类型的库文件:标准库和第三方的库。标准库是随Go安装的时候放在go安装目录下的($GOROOT/src/
),第三方库是放在workspace下的。关于workspace后文再说。
共享库可以被import导入(例如fmt包)。由于导入操作是在编译期间实现的,共享库中不应该包含任何输出型语句。
Go中对库文件要求比较严格,或者说强制性的规范。它要求库文件中package声明的包名必须和目录名称相同,且同一个目录下只允许有一个包,但同一个目录下可以有多个库文件片段,只不过这些库文件中必须都使用package声明它的包名为目录名。例如:
src/mycode
|- first.go
|- second.go
|- third.go
如果这三个文件都是库文件,则它们都必须且只能使用package mycode
声明自己的包为mycode。go build
的时候,会将它们合并起来。如果声明的包名不是mycode,go build会直接忽略它。
当然,对main包就无所谓了,它不是库文件,可以放在任何地方,对目录名没有要求。但如果使用go install
,则有额外的要求,见后文。
库文件中的大小写命名
Go通过名称首字母的大小写决定属性是否允许导出:
- 首字母大写的属性是允许导出的属性
- 首字母小写的属性不允许被导出
所以当库文件被导入时,只有这个库文件中以大写字母开头的常量、变量、函数等才会被导出,才可以在其他文件中使用。
例如,库文件abc.go中:
func first() {}
func Second() {}
当导入这个包的时候,由于first()函数首字母小写,外界无法使用它,它只能在自己的包abc.go中使用,对外界不可见。大写字母开头的Second()函数会被导入,所以可用。
工作空间(workspace)
速览
- 通过环境变量
GOPATH
设置workspace的路径 - Go编程人员一般将它们的Go代码放在一个
workspace
下,当然,这不是必须的 - workspace包含一个或多个版本控制系统的仓库(如git)
- 每个仓库包含一个或多个package
- 每个package由单个目录下的一个或多个Go源文件组成,它们都必须声明目录名作为它们的包名
- package的目录路径决定导入包时import的路径
Go和其它编程语言在组织项目的时候有所不同,其它语言一般每个项目都有一个单独的workspace,且workspace一般和版本控制仓库进行绑定。
现在设置GOPATH环境变量,假设设置为/gocode
echo 'export GOPATH=/gocode' >>/etc/profile.d/gopath.sh
chmod +x /etc/profile.d/gopath.sh
source /etc/profile.d/gopath.sh
go env GOPATH
确定是否正确:
$ go env GOPATH
/gocode
workspace目录结构
每个workspace都是一个目录,这个目录下至少包含三个目录:
- src:该目录用于存放Go源代码文件(也称为命令的源文件)
- bin:该目录用于存放可执行命令(即构建后可执行的二进制go程序,也称为命令文件)
- pkg:该目录用于存放共享库文件(即构建后非可执行程序的库包,也称为包对象文件)
括号中给的名称是go官方文档中常见的别名称呼。
所以,先创建这3个目录
mkdir -p /gocode/{src,pkg,bin}
GOPATH和GOROOT环境变量
GOPATH环境变量指定workspace的位置,用来指示go从哪里搜索go源文件/包,例如import时从哪个路径搜索包并导入。GOROOT环境变量用于指定go的安装位置。go需要导入包时,会从GOPATH和GOROOT所设置的位置处搜索包。
默认位置为$HOME/go
(Unix)或%USERPROFILE\go%
(Windows)。可以手动设置GOPATH环境变量的路径从而指定workspace的位置,可以指定为多个目录,多个目录时使用冒号分隔目录(Unix系统)或使用分号分隔目录(Windows系统)。注意,绝对不能将其设置为go的安装目录,即不能和GOROOT环境变量重复。
例如,windows下设置d:\gocode
目录为GOPATH的路径:
setx GOPATH d:\gocode
Unix下设置$HOME/gocode
目录为GOPATH的路径:
mkdir ~/gocode
export GOPATH=~/gocode
echo 'GOPATH=~/gocode' >>~/.bashrc
go env
或go env GOPATH
命令可以输出当前有效的GOPATH路径。
$ go env | grep GOPATH
GOPATH="/root/gocode"
$ go env GOPATH
/root/gocode
go build
先写两个go文件,一个是可执行go文件test.go,一个是共享库strutils.go,将它们放在workspace的src下。
$ mkdir -p $GOPATH/src/{hello,strutils}
$ tree -C
.
├── bin
├── pkg
├── src
│ ├── hello
│ │ └── test.go
│ └── strutils
│ └── strutils.go
注意,上面故意将test.go放在名为hello的目录下,可以将其放在src下的任何非库文件目录下(例如不能放进strutils目录下),名称不要求。
hello/test.go的内容如下:
package main
import (
"fmt"
"strutils"
)
func main() {
fmt.Println("Hello World")
fmt.Println(strutils.ToUpperCase("hello world"))
}
strutils/strutils.go的内容如下:
package strutils
import (
"strings"
)
func ToUpperCase(s string) string{
return strings.ToUpper(s)
}
func ToLowerCase(s string) string{
return strings.ToLower(s)
}
go build
可以用于编译,编译时会对import导入的包进行搜索,搜索的路径为标准库所在路径$GOROOT/src
、workspace下的src目录。它只会生成额外的可执行文件放在当前目录下,不会生成额外的库文件。但需要注意,生成的可执行文件名称可能会出乎意料:
例如进入到目录src/hello
下,对test.go的文件进行编译,以下三种build路径都可用成功编译:
cd src/hello
go build # 生成的可执行文件名为hello
go build . # 生成的可执行文件名为hello
go build test.go # 生成的可执行文件名为test
前两者是等价的,当go build
以目录的形式进行编译,则生成的可执行文件名为目录名。当go build
以go代码文件名的方式进行编译,则生成的可执行程序名为go源码文件名(去掉后缀.go或增加后缀.exe)。
go install
go还有一个工具install,go install
的操作称为安装,将文件安装到合适的位置。go install
时会先进行编译,然后将编译后的二进制文件保存到workspace的bin目录下,将编译后的库文件(称为包对象文件,以".a"为后缀)放在pkg目录下。
注意,go install
时必须先进入到$GOPATH/src
下,且只能对目录进行操作,不能对具体的go文件操作,因为go认为包和目录名相同。给go install
指定一个目录名,就表示编译这个包名。
例如,对src/hello下的test.go进行安装,由于它导入了strutils包,所以会自动将strutils也安装好:
$ cd $GOPATH/src
$ go install hello
$ tree $GOPATH
/gocode
├── bin
│ └── hello # 二进制程序文件名为hello,而非test
├── pkg
│ └── linux_amd64
│ └── strutils.a # 库文件
└── src
├── hello
│ └── test.go
└── strutils
└── strutils.go
还可以单独对库文件进行安装:
$ rm -rf $GOPATH/bin/* $GOPATH/pkg/*
$ cd $GOPATH/src
$ go install strutils
/gocode
├── bin
├── pkg
│ └── linux_amd64
│ └── strutils.a
└── src
├── hello
│ └── test.go
└── strutils
└── strutils.go
如果省略目录名,则表示对当前目录下的包进行安装:
$ cd $GOPATH/src/hello
$ go install
再次提醒,go install
前先进入到$GOPATH/src
目录下。
由于go install可以直接安装二进制文件到$GOPATH/bin
,所以出于方便执行这些二进制程序,可以将这个目录放进PATH环境变量。
$ export PATH=$PATH:`go env GOPATH`/bin
构建go程序的规范建议
1.由于可以将所有go项目放在同一个$GOPATH
目录下,为了区分src下的项目目录和库文件目录,建议将每个项目目录设置深一点。
例如:
bin
pkg
src
|- /first/project
|- main.go
|- myliba
|- a.go
|- b.go
|- mylibb
|- c.go
|- d.go
|- /second/project
|- main.go
|- lib
|- a.go
|- b.go
2.go install时,先进入到项目目录下。
3.库文件的名称(也是目录名)要选取合理,尽量短,但却尽量见名知意,也尽量减少名称重复的几率。
例如util这种名称到处都是,可以修改为numutil、nameutil等。
Go基础系列:构建go程序的更多相关文章
- 如何在Visual Studio 2017中使用C# 7+语法 构建NetCore应用框架之实战篇(二):BitAdminCore框架定位及架构 构建NetCore应用框架之实战篇系列 构建NetCore应用框架之实战篇(一):什么是框架,如何设计一个框架 NetCore入门篇:(十二)在IIS中部署Net Core程序
如何在Visual Studio 2017中使用C# 7+语法 前言 之前不知看过哪位前辈的博文有点印象C# 7控制台开始支持执行异步方法,然后闲来无事,搞着,搞着没搞出来,然后就写了这篇博文,不 ...
- .NET Core 小程序开发零基础系列(2)——小程序服务通知(模板消息)
基于上一篇文件“.NET Core 小程序开发零基础系列(1)——开发者启用并校验牵手成功”的反映,个人觉得效果很不错,大家对公众号开发还是有很大需求的,同时也收到了很多同学的问题,后面我也会通过实战 ...
- JVM基础系列第2讲:Java 虚拟机的历史
说起 Java 虚拟机,许多人就会将其与 HotSpot 虚拟机等同看待.但实际上 Java 虚拟机除了 HotSpot 之外,还有 Sun Classic VM.Exact VM.BEA JRock ...
- Spring基础系列--AOP织入逻辑跟踪
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9619910.html 其实在之前的源码解读里面,关于织入的部分并没有说清楚,那些前置.后 ...
- Spring基础系列-AOP源码分析
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9560803.html 一.概述 Spring的两大特性:IOC和AOP. AOP是面向切 ...
- python基础系列教程——Python3.x标准模块库目录
python基础系列教程——Python3.x标准模块库目录 文本 string:通用字符串操作 re:正则表达式操作 difflib:差异计算工具 textwrap:文本填充 unicodedata ...
- 夯实Java基础系列6:一文搞懂抽象类和接口,从基础到面试题,揭秘其本质区别!
目录 抽象类介绍 为什么要用抽象类 一个抽象类小故事 一个抽象类小游戏 接口介绍 接口与类相似点: 接口与类的区别: 接口特性 抽象类和接口的区别 接口的使用: 接口最佳实践:设计模式中的工厂模式 接 ...
- 夯实Java基础系列9:深入理解Class类和Object类
目录 Java中Class类及用法 Class类原理 如何获得一个Class类对象 使用Class类的对象来生成目标类的实例 Object类 类构造器public Object(); register ...
- C#基础系列——Attribute特性使用
前言:上篇 C#基础系列——反射笔记 总结了下反射得基础用法,这章我们来看看C#的另一个基础技术——特性. 1.什么是特性:就博主的理解,特性就是在类的类名称.属性.方法等上面加一个标记,使这些类.属 ...
随机推荐
- 底图与蒙版的过渡效果transition
我用2种方法写了底图与蒙版的过渡效果 方法一:用js方法 <!DOCTYPE html> <html> <head> <meta http-equiv=&qu ...
- kubernetes1.7.6 ha高可用部署
写在前面: 1. 该文章部署方式为二进制部署. 2. 版本信息 k8s 1.7.6,etcd 3.2.9 3. 高可用部分 etcd做高可用集群.kube-apiserver 为无状态服务使用hap ...
- python之路(四)-set集合
set集合 set是一个无序且不重复的元素集合优点:访问速度快,解决重复问题 l1 = [1,2,34,5,6,7,4,3,3,] s2 = set(l1) #可以以列表元祖等作为参数传进来,set集 ...
- <笔记>TP5的save方法返回值
用save方法来更新数据时,若更新前后数据没有改变则返回0,更新成功返回影响行数,更新失败返回false 若想要数据没改变时提示修改成功,则需要严格判断 if(结果!==false){提示成功}而不是 ...
- python基础自学 第二天
注释 分类 单行注释 多行注释 作用 使用自己熟悉的语言,在程序中对某些代码进行标注说明,增强程序可读性 单行注释(行注释) 以 # 开头,#右边所有的东西就被当成说明文字,而不是要执行的程序,只是说 ...
- 转:Override vs Overload
重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变.即外壳不变,核心重写! 重写的好处在于子类可以根据需要,定义特定于自己的行为. 也就是说子类 ...
- AVPass技术分析:银行劫持类病毒鼻祖BankBot再度来袭,如何绕过谷歌play的杀毒引擎?
背景 近期,一批伪装成flashlight.vides和game的应用,发布在google play官方应用商店.经钱盾反诈实验室研究发现,该批恶意应用属于新型BankBot.Bankbot家族算得上 ...
- VSCode插件开发全攻略(十)打包、发布、升级
更多文章请戳VSCode插件开发全攻略系列目录导航. 发布方式 插件开发完了,如何发布出去分享给他人呢?主要有3种方法: 方法一:直接把文件夹发给别人,让别人找到vscode的插件存放目录并放进去,然 ...
- 【高速接口-RapidIO】2、RapidIO串行物理层的包与控制符号
一.RapidIO串行物理层背景介绍 上篇博文提到RapidIO的物理层支持串行物理层与并行物理层两种,由于Xilinx 部分FPGA内部已经集成了串行高速收发器,所以用FPGA实现RapidIO大多 ...
- 【渗透攻防Web篇】SQL注入攻击高级
前言 前面我们学习了如何寻找,确认,利用SQL注入漏洞的技术,本篇文章我将介绍一些更高级的技术,避开过滤,绕开防御.有攻必有防,当然还要来探讨一下SQL注入防御技巧. 目录 第五节 避开过滤方法总结 ...