0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

go语言底层对于命令行参数的解析

冬至子 来源:我的小碗汤 作者:小碗汤 2022-10-11 10:50 次阅读

前段时间有群友在群里问一个go语言的问题:

就是有一个main.go的main函数里调用了另一个demo.go里的hello()函数。其中main.go和hello.go同属于main包。但是在main.go的目录下执行go run main.go却报hello函数没有定义的错:

pYYBAGNE2XiAbE_6AABJdSb2xQ0407.png

代码结构如下:

**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,试了确实是可以的。

poYBAGNE2YqAUSTjAAA_Q8R_NUQ625.png

虽然是个很简单的问题,但是也涉及到了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个命令提供了支持:

poYBAGNE2aGAGmHEAADVufp8n54966.png

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

pYYBAGNE2bWAP7QzAABkuYqe9Uw995.png

在执行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

bVeVF


就会进入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函数,导致报错。




审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • main
    +关注

    关注

    0

    文章

    38

    浏览量

    6163
  • go语言
    +关注

    关注

    1

    文章

    158

    浏览量

    9047
收藏 人收藏

    评论

    相关推荐

    Shell脚本命令行解析

    Shell脚本解析命令行参数——Argparse(填坑)
    发表于 04-03 11:34

    cmd网络经典命令行

    cmd网络经典命令行:
    发表于 06-11 15:17 36次下载
    cmd网络经典<b class='flag-5'>命令行</b>

    C语言入门教程-命令行参数

    命令行参数 在C中,获取用户输入的命令行参数是很方便的。程序的主函数会接受一个argv参数。有许多高级的库函数都会用到argv结构,所以了
    发表于 07-29 14:22 2610次阅读

    caxa命令行中的应用

    caxa命令行中的应用 命令行对于大多用户来说往往只是输入数据的作用,但是其中的奥妙还有很多,下面就给大家
    发表于 10-18 18:18 2295次阅读

    博达环网配置命令行

    博达工业交换要环网配置命令行
    发表于 12-27 16:24 0次下载

    CMD的命令行高级教程

    CMD的命令行高级教程
    发表于 10-24 08:31 30次下载
    CMD的<b class='flag-5'>命令行</b>高级教程

    自己动手实现命令行解析

    一、介绍嵌入式里面经常会自己做命令行,这里分享一个命令解析器代码实现二、代码实现mycmd.c
    发表于 12-22 18:51 13次下载
    自己动手实现<b class='flag-5'>命令行</b><b class='flag-5'>解析</b>器

    如何从命令行获取和解析参数

    这是一篇威廉希尔官方网站 干货快文,能够快速阅读完。文章内容是关于如何从命令行获取和解析参数,包括SystemVerilog本身支持的系统函数和UVM提供的函数封装,并给出示例代码和仿真结果。
    的头像 发表于 05-30 14:05 2957次阅读

    uvm命令行传递参数的小技巧

    当我们在创建动态仿真case时,使用命令行参数可以非常方便地控制DUT和TB的行为,比如配置寄存器、控制激励的发送数量、打开或关闭某些scoreboard等。
    的头像 发表于 08-19 11:53 5603次阅读

    Shell命令行解释器简介

    Shell 是一个命令行解释器,Shell 为用户提供了与设备进行命令行交互的方式,用户通过串口、以太网、无线等方式将命令传输给具有 Shell 功能的设备,设备会解析
    的头像 发表于 08-19 17:20 3223次阅读

    Golang基于flag库实现一个命令行工具

    Golang 标准库中的 flag 库提供了解析命令行选项的能力,我们可以基于此来开发命令行工具。
    的头像 发表于 10-28 09:26 1296次阅读

    Linux命令行与shell脚本编写

    Linux命令行与shell脚本编写
    发表于 01-11 16:50 4次下载

    介绍Go里面经常使用到的命令行工具

    优秀的工具配合熟练的使用,往往可以让开发效率大幅度提升,本小节介绍 Go 里面经常使用到的命令行工具。
    的头像 发表于 05-22 16:58 1292次阅读
    介绍<b class='flag-5'>Go</b>里面经常使用到的<b class='flag-5'>命令行</b>工具

    linux切换到命令行模式

    在Linux中,可以通过以下步骤切换到命令行模式: 打开终端。可以在应用菜单中找到终端或命令行终端。 在终端中输入命令“exit”或“logout”,然后按回车键。 系统会提示您输入管理员密码。输入
    的头像 发表于 11-13 16:47 1781次阅读

    idea如何输入命令行参数

    在许多软件开发和系统管理的任务中,我们经常需要向应用程序传递命令行参数命令行参数是在运行时传递给程序的值,用于指定程序的行为和配置选项。本文将详细介绍如何在不同的编程
    的头像 发表于 12-06 15:01 1150次阅读