golang初探与命令源码分析
前段时间有群友在群里问一个go语言的问题:
就是有一个main.go的main函数里调用了另一个demo.go里的hello()函数。其中main.go和hello.go同属于main包。但是在main.go的目录下执行go run main.go却报hello函数没有定义的错:

代码结构如下:
**gopath ---- src**
**----gohello**
**----hello.go**
** ----main.go**
main.go如下:
package main
import "fmt"
func main() {
fmt.Println("my name is main")
hello()
}
hello.go如下:
package main
import "fmt"
func hello() {
fmt.Println("my name is hello")
}
当时我看了以为是他GOPATH配置的有问题,然后自己也按照这样试了一下,报同样的错,在网上查了,也有两篇文章是关于这个错的,也提供了解决方法,即用go run main.go hello.go,试了确实是可以的。

虽然是个很简单的问题,但是也涉及到了go语言底层对于命令行参数的解析。那就来分析一下语言底层的实现吧,看一下底层做了什么,为什么报这个错?
分析:
以下使用到的Go SDK版本为1.8.3
该版本中go支持的基本命令有以下16个:
build compile packages and dependencies
clean remove object files
doc show documentation for package or symbol
env print Go environment information
bug start a bug report
fix run go tool fix on packages
fmt run gofmt on package sources
generate generate Go files by processing source
get download and install packages and dependencies
install compile and install packages and dependencies
list list packages
run compile and run Go program
test test packages
tool run specified go tool
version print Go version
vet run go tool vet on packages
在Go SDK的src/cmd/go包下有main.go文件中,Command类型的commands数组对该16个命令提供了支持:

我们首先知道go语言的初始化流程如下:

在执行main.go中的主函数main之前,对import进来的包按顺序初始化,最后初始化main.go中的类型和变量,当初始化到commands数组时,由于cmdRun定义在于main.go同包下的run.go中,那么就先去初始化run.go中的变量和init方法,如下代码,先把cmdRun初始化为Command类型,然后执行init()函数。
var cmdRun = &Command{
UsageLine: "run [build flags] [-exec xprog] gofiles... [arguments...]",
Short: "compile and run Go program",
Long: `
Run compiles and runs the main package comprising the named Go source files.
A Go source file is defined to be a file ending in a literal ".go" suffix.
By default, 'go run' runs the compiled binary directly: 'a.out arguments...'.
If the -exec flag is given, 'go run' invokes the binary using xprog:
'xprog a.out arguments...'.
If the -exec flag is not given, GOOS or GOARCH is different from the system
default, and a program named go_$GOOS_$GOARCH_exec can be found
on the current search path, 'go run' invokes the binary using that program,
for example 'go_nacl_386_exec a.out arguments...'. This allows execution of
cross-compiled programs when a simulator or other execution method is
available.
For more about build flags, see 'go help build'.
See also: go build.
`,
}
func init() {
cmdRun.Run = runRun // break init loop
addBuildFlags(cmdRun)
cmdRun.Flag.Var((*stringsFlag)(&execCmd), "exec", "")
}
init()中,将runRun(其实类型是一个方法,用于处理run后的参数)赋值给cmdRu.run,addBuildFlags(cmdRun)主要是给run后面增加命令行参数(如:-x是打印其执行过程中用到的所有命令,同时执行它们)。其他15个命令和cmdRun类似,各有各的run实现。
下来主要看main.go中main的这块代码:
for _, cmd := range commands {
if cmd.Name() == args[0] && cmd.Runnable() {
cmd.Flag.Usage = func() { cmd.Usage() }
if cmd.CustomFlags {
args = args[1:]
} else {
cmd.Flag.Parse(args[1:])
args = cmd.Flag.Args()
}
cmd.Run(cmd, args)
exit()
return
}
}
这块代码遍历commands数组,当遍历到cmdRun时,cmd.Name()其实就是拿到cmdRun.UsageLine的第一个单词run

就会进入if分支,由于cmd.CustomFlags没有初始化故为false,走else分支,然后开始解析args命令行参数,args[1:]即取run后的所有参数。然后去执行cmd.Run(cmd, args),对于cmdRun来说,这里执行的就是run.go中init()的第一行赋值cmdRun.run(上面说了,这是一个函数,不同命令实现方式不同),即去执行run.go中的runRun函数,该函数主要是将命令行参数当文件去处理,如果是_test为后缀的,即测试文件,直接报错。如果是目录也直接报错(而且go run后面只能包含一个含main函数的go文件)。注意到有这么一行:
p := goFilesPackage(files)
goFilesPackage(files)除了校验文件类型和后缀,还会入栈,加载,出栈等操作,由于启动的时候没有传递hello.go,所以系统加载main.go时找不到hello函数,导致报错。
本公众号免费提供csdn下载服务,海量IT学习资源,如果你准备入IT坑,励志成为优秀的程序猿,那么这些资源很适合你,包括但不限于java、go、python、springcloud、elk、嵌入式 、大数据、面试资料、前端 等资源。同时我们组建了一个技术交流群,里面有很多大佬,会不定时分享技术文章,如果你想来一起学习提高,可以公众号后台回复【2】,免费邀请加技术交流群互相学习提高,会不定期分享编程IT相关资源。
扫码关注,精彩内容第一时间推给你

golang初探与命令源码分析的更多相关文章
- memcached学习笔记——存储命令源码分析下篇
上一篇回顾:<memcached学习笔记——存储命令源码分析上篇>通过分析memcached的存储命令源码的过程,了解了memcached如何解析文本命令和mencached的内存管理机制 ...
- memcached学习笔记——存储命令源码分析上篇
原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command ...
- debug:am、cmd命令源码分析
debug:am.cmd命令源码分析 目录 debug:am.cmd命令源码分析 am命令的实现 手机里的am am.jar cmd命令的实现 手机里的cmd cmd activity cmd.cpp ...
- TiDB show processlist命令源码分析
背景 因为丰巢自去年年底开始在推送平台上尝试了TiDB,最近又要将承接丰巢所有交易的支付平台切到TiDB上.我本人一直没有抽出时间对TiDB的源码进行学习,最近准备开始一系列的学习和分享.由于我本人没 ...
- storm shell命令源码分析-shell_submission.clj
当我们在shell里执行storm shell命令时会调用shell_submission.clj里的main函数.shell_submission.clj如下: shell_submission.c ...
- Django(三)runserver 命令源码分析
应用环境 windows7 pycharm2018 profession python3.6 django2.0 我们在pycharm 启动django项目时,常常有这么一个命令操作: python ...
- golang slice 使用及源码分析
1.先做个小实验 func main(){ s1:=make([]int,0,10) s1=[]int{1,2,3} ss:=make([]int,0,10) ss = s1[1:] for i:=0 ...
- redis源码分析之事务Transaction(上)
这周学习了一下redis事务功能的实现原理,本来是想用一篇文章进行总结的,写完以后发现这块内容比较多,而且多个命令之间又互相依赖,放在一篇文章里一方面篇幅会比较大,另一方面文章组织结构会比较乱,不容易 ...
- redis源码分析之有序集SortedSet
有序集SortedSet算是redis中一个很有特色的数据结构,通过这篇文章来总结一下这块知识点. 原文地址:http://www.jianshu.com/p/75ca5a359f9f 一.有序集So ...
随机推荐
- WS的发布与调用
WebService—CXF整合Spring实现接口发布和调用过程 https://www.cnblogs.com/domi22/p/8094517.html spring 集成cxf 第二弹( ...
- Go pprof性能调优
在计算机性能调试领域里,profiling 是指对应用程序的画像,画像就是应用程序使用 CPU 和内存的情况. Go语言是一个对性能特别看重的语言,因此语言中自带了 profiling 的库,这篇文章 ...
- mybatis-generator:generate 生成代码配置踩坑详解
mybatis-generator:generate 生成代码配置踩坑不少,在此留下笔记以便后续填坑 一.mysql返回时间问题 错误信息: [ERROR] Failed to execute goa ...
- jps虚拟机进程状态工具
jps(JVM Process Status Tool) jps工具可以列出正在运行的虚拟机进程,并显示虚拟机执行主类名称以及这些进程的本地虚拟机唯一ID(Local Virtual Machine ...
- 【第十二篇】微信支付(APP)集成时碰到的问题(.net提示“无权限”、iOS跳转到微信支付页面中间只有一个“确定”按钮)(转)
直入主题之前,请容我吐槽一下微*的官方东西:ASDFQ%#$%$#$%^FG@#$%DSFQ#$%.......:吐槽玩了!大家心照就好. 要完成手机APP跳转到微信的APP进行微信支付,需要进行如下 ...
- All Of ACM
数据结构和算法专栏,我会什么写什么 = = 不定时更新 一.数据结构 树状数组详解 线段树详解 二.算法 KMP算法 三.板子 我的代码模板 大整数模板 四.题解报告 几道STL题目(FJUT - ...
- Cookie的删除
1.设置一个Cookie,与要删除的Cookie同名,并将有效时间设置为0: protected void doGet(HttpServletRequest request, HttpServletR ...
- 构建企业级数据湖?Azure Data Lake Storage Gen2实战体验(中)
引言 相较传统的重量级OLAP数据仓库,“数据湖”以其数据体量大.综合成本低.支持非结构化数据.查询灵活多变等特点,受到越来越多企业的青睐,逐渐成为了现代数据平台的核心和架构范式. 因此数据湖相关服务 ...
- Spark比MR快是因为在内存中计算?错!
MapReduce 就像一台又慢又稳的老爷车,虽然距离 MapReduce 面市到现在已经过去了十几年的时间,但它始终没有被淘汰,任由大数据技术日新月异.蓬蓬勃勃.花里胡哨地发展,这个生态圈始终有它的 ...
- jenkins自动化部署项目5 -- 系统管理-系统设置ssh配置
[系统管理]-[系统设置] 如果应用服务(前端后台)要部署在linux服务器上,我选择的是用ssh 为了jenkins登录远程登录linux服务器可以免密登录,先配置公钥和私钥: 我是在windows ...