命令源码文件

本文深入探讨Go语言中命令源码文件的用途及其编写,讲解如何接收和解析命令参数,以及如何自定义命令参数的使用说明。通过示例代码展示了如何使用flag包,包括flag.StringVar、flag.Parse等函数,以及如何通过重新定义flag.Usage和创建私有FlagSet实现更灵活的参数处理。此外,还介绍了godoc命令的使用。

我们已经知道,环境变量 GOPATH 指向的是一个或多个工作区,每个工作区中都会有以代码包为基本组织形式的源码文件。

这里的源码文件又分为三种,即:命令源码文件、库源码文件和测试源码文件,它们都有着不同的用途和编写规则。 我在预习篇介绍过这三种文件的基本情况。今天,我们就沿着命令源码文件的知识点,展开更深层级的学习。


一旦开始学习用编程语言编写程序,我们就一定希望在编码的过程中及时地得到反馈,只有这样才能清楚对错。实际上,我们的有效学习和进步,都是通过不断地接受反馈和执行修正实现的。

对于 Go 语言学习者来说,你在学习阶段中,也一定会经常编写可以直接运行的程序。这样的程序肯定会涉及命令源码文件的编写,而且,命令源码文件也可以很方便地用go run命令启动。

那么,我今天的问题就是:命令源码文件的用途是什么,怎样编写它?

这里,我给出你一个参考的回答:命令源码文件是程序的运行入口,是每个可独立运行的程序必须拥有的。我们可以通过构建或安装,生成与其对应的可执行文件,后者一般会与该命令源码文件的直接父目录同名。

如果一个源码文件声明属于main包,并且包含一个无参数声明且无结果声明的main函数,那么它就是命令源码文件。 就像下面这段代码:

package main

import "fmt"

func main() {
fmt.Println("Hello, world!")
}

如果你把这段代码存成 demo1.go 文件,那么运行go run demo1.go命令后就会在屏幕(标准输出)中看到Hello, world!

当需要模块化编程时,我们往往会将代码拆分到多个文件,甚至拆分到不同的代码包中。但无论怎样,对于一个独立的程序来说,命令源码文件永远只会也只能有一个。如果有与命令源码文件同包的源码文件,那么它们也应该声明属于main包。

问题解析

命令源码文件如此重要,以至于它毫无疑问地成为了我们学习 Go 语言的第一助手。不过,只会打印Hello, world是远远不够的,咱们千万不要成为“Hello, world”党。既然决定学习 Go 语言,你就应该从每一个知识点深入下去。

无论是 Linux 还是 Windows,如果你用过命令行(command line)的话,肯定就会知道几乎所有命令(command)都是可以接收参数(argument)的。通过构建或安装命令源码文件,生成的可执行文件就可以被视为“命令”,既然是命令,那么就应该具备接收参数的能力。

下面,我就带你深入了解一下与命令参数的接收和解析有关的一系列问题。

知识精讲

1. 命令源码文件怎样接收参数

我们先看一段不完整的代码:

package main

import (
// 需在此处添加代码。[1]
"fmt"
)

var name string

func init() {
// 需在此处添加代码。[2]
}

func main() {
// 需在此处添加代码。[3]
fmt.Printf("Hello, %s!\n", name)
}

如果邀请你帮助我,在注释处添加相应的代码,并让程序实现”根据运行程序时给定的参数问候某人”的功能,你会打算怎样做?

如果你知道做法,请现在就动手实现它。如果不知道也不要着急,咱们一起来搞定。

首先,Go 语言标准库中有一个代码包专门用于接收和解析命令参数。这个代码包的名字叫flag

我之前说过,如果想要在代码中使用某个包中的程序实体,那么应该先导入这个包。因此,我们需要在[1]处添加代码"flag"。注意,这里应该在代码包导入路径的前后加上英文半角的引号。如此一来,上述代码导入了flagfmt这两个包。

其次,人名肯定是由字符串代表的。所以我们要在[2]处添加调用flag包的StringVar函数的代码。就像这样:

flag.StringVar(&name, "name", "everyone", "The greeting object.")

函数flag.StringVar接受 4 个参数。第 1 个参数是用于存储该命令参数的值的地址,具体到这里就是在前面声明的变量name的地址了,由表达式&name表示。

第 2 个参数是为了指定该命令参数的名称,这里是name。第 3 个参数是为了指定在未追加该命令参数时的默认值,这里是everyone

至于第 4 个函数参数,即是该命令参数的简短说明了,这在打印命令说明时会用到。

顺便说一下,还有一个与flag.StringVar函数类似的函数,叫flag.String。这两个函数的区别是,后者会直接返回一个已经分配好的用于存储命令参数值的地址。如果使用它的话,我们就需要把

var name string

改为

var name = flag.String("name", "everyone", "The greeting object.")

所以,如果我们使用flag.String函数就需要改动原有的代码。这样并不符合上述问题的要求。

再说最后一个填空。我们需要在[3]处添加代码flag.Parse()。函数flag.Parse用于真正解析命令参数,并把它们的值赋给相应的变量。

对该函数的调用必须在所有命令参数存储载体的声明(这里是对变量name的声明)和设置(这里是在[2]处对flag.StringVar函数的调用)之后,并且在读取任何命令参数值之前进行。

正因为如此,我们最好把flag.Parse()放在main函数的函数体的第一行。

2. 怎样在运行命令源码文件的时候传入参数,又怎样查看参数的使用说明

如果我们把上述代码存成名为 demo2.go 的文件,那么运行如下命令就可以为参数name传值:

go run demo2.go -name="Robert"

运行后,打印到标准输出(stdout)的内容会是:

Hello, Robert!

另外,如果想查看该命令源码文件的参数说明,可以这样做:

$ go run demo2.go --help

其中的

MY_ZUES_CHAR

go run

Usage of /var/folders/ts/7lg_tl_x2gd_k1lm5g_48c7w0000gn/T/go-build155438482/b001/exe/demo2:
 -name string
    The greeting object. (default "everyone")
exit status 2

你可能不明白下面这段输出代码的意思。

/var/folders/ts/7lg_tl_x2gd_k1lm5g_48c7w0000gn/T/go-build155438482/b001/exe/demo2

这其实是go run命令构建上述命令源码文件时临时生成的可执行文件的完整路径。

如果我们先构建这个命令源码文件再运行生成的可执行文件,像这样:

$ go build demo2.go
$ ./demo2 --help

那么输出就会是

Usage of ./demo2:
 -name string
    The greeting object. (default "everyone")

3. 怎样自定义命令源码文件的参数使用说明

这有很多种方式,最简单的一种方式就是对变量flag.Usage重新赋值。flag.Usage的类型是func(),即一种无参数声明且无结果声明的函数类型。

flag.Usage变量在声明时就已经被赋值了,所以我们才能够在运行命令go run demo2.go --help时看到正确的结果。

注意,对flag.Usage的赋值必须在调用flag.Parse函数之前。

现在,我们把 demo2.go 另存为 demo3.go,然后在main函数体的开始处加入如下代码。

flag.Usage = func() {
 fmt.Fprintf(os.Stderr, "Usage of %s:\n", "question")
 flag.PrintDefaults()
}

那么当运行

$ go run demo3.go --help

后,就会看到

Usage of question:
 -name string
    The greeting object. (default "everyone")
exit status 2

现在再深入一层,我们在调用flag包中的一些函数(比如StringVarParse等等)的时候,实际上是在调用flag.CommandLine变量的对应方法。

flag.CommandLine相当于默认情况下的命令参数容器。所以,通过对flag.CommandLine重新赋值,我们可以更深层次地定制当前命令源码文件的参数使用说明。

现在我们把main函数体中的那条对flag.Usage变量的赋值语句注销掉,然后在init函数体的开始处添加如下代码:

flag.CommandLine = flag.NewFlagSet("", flag.ExitOnError)
flag.CommandLine.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", "question")
flag.PrintDefaults()
}

再运行命令go run demo3.go --help后,其输出会与上一次的输出的一致。不过后面这种定制的方法更加灵活。比如,当我们把为flag.CommandLine赋值的那条语句改为

flag.CommandLine = flag.NewFlagSet("", flag.PanicOnError)

后,再运行go run demo3.go --help命令就会产生另一种输出效果。这是由于我们在这里传给flag.NewFlagSet函数的第二个参数值是flag.PanicOnErrorflag.PanicOnErrorflag.ExitOnError都是预定义在flag包中的常量。

flag.ExitOnError的含义是,告诉命令参数容器,当命令后跟--help或者参数设置的不正确的时候,在打印命令参数使用说明后以状态码2结束当前程序。

状态码2代表用户错误地使用了命令,而flag.PanicOnError与之的区别是在最后抛出“运行时恐慌(panic)”。

上述两种情况都会在我们调用flag.Parse函数时被触发。顺便提一句,“运行时恐慌”是 Go 程序错误处理方面的概念。关于它的抛出和恢复方法,我在本专栏的后续部分中会讲到。

下面再进一步,我们索性不用全局的flag.CommandLine变量,转而自己创建一个私有的命令参数容器。我们在函数外再添加一个变量声明:

var cmdLine = flag.NewFlagSet("question", flag.ExitOnError)

然后,我们把对flag.StringVar的调用替换为对cmdLine.StringVar调用,再把flag.Parse()替换为cmdLine.Parse(os.Args[1:])

其中的os.Args[1:]指的就是我们给定的那些命令参数。这样做就完全脱离了flag.CommandLine*flag.FlagSet类型的变量cmdLine拥有很多有意思的方法。你可以去探索一下。我就不在这里一一讲述了。

这样做的好处依然是更灵活地定制命令参数容器。但更重要的是,你的定制完全不会影响到那个全局变量flag.CommandLine

总结

恭喜你!你现在已经走出了 Go 语言编程的第一步。你可以用 Go 编写命令,并可以让它们像众多操作系统命令那样被使用,甚至可以把它们嵌入到各种脚本中。

虽然我为你讲解了命令源码文件的基本编写方法,并且也谈到了为了让它接受参数而需要做的各种准备工作,但这并不是全部。

别担心,我在后面会经常提到它的。另外,如果你想详细了解flag包的用法,可以到这个网址查看文档。或者直接使用godoc命令在本地启动一个 Go 语言文档服务器。怎样使用godoc命令?你可以参看这里

MATLAB源代码MATLAB源码大集合220MB上千个源码文件: 2013全国大学生数学建模B题matlab代码.rar 802.11a OFDM MATLAB仿真代码!(推荐).rar MATLAB DCT水印源程序代码.rar MATLAB GUI 实例.rar matlab GUI实例.rar MATLAB GUI实现动态画图曲线的源程序代码.rar matlab.rar matlab7.0从入门到精通-光盘程序.rar Matlab_车牌识别.zip MATLAB中colorbar的设置 源程序代码.rar matlab中文字符的识别代码.rar MATLAB中的基本语法和语句示例代码.rar MATLAB使用欧拉Euler法求解微分方程组 源程序代码.rar MATLAB光通过三稜镜色散动画.rar Matlab十大算法源代码.rar Matlab图像处理与界面编程宝典源码.rar matlab图像处理代码.rar MATLAB图像处理实现直线识别(拟合角平分线).rar MATLAB图像处理实现螺纹识别 源程序代码.rar MATLAB图像配准程序.rar MATLAB在数学建模中的应用(上下 源程序).rar MATLAB夜间车牌识别程序.rar MATLAB实现不同插值方法的GUI界面设计 源程序代码.rar MATLAB实现偏微分方程的差分计算 源程序代码.rar MATLAB实现图像去噪 滤波 锐化 边缘检测.rar matlab实现图像拼接的代码.rar MATLAB实现学生成绩查询系统 源代码程序(1).rar MATLAB实现学生成绩查询系统 源代码程序.rar MATLAB实现灰度预测模型的源代码.rar MATLAB实现线性拟合和相关系数 源程序代码.rar matlab实现视频中动态目标跟踪.rar matlab实用程序300例.rar MATLAB寻找素数的源程序代码.rar MATLAB小波分析—张德丰(代码).rar MATLAB建模 人口增长模型 源程序代码.rar MATLAB文字连通域源程序代码.rar MATLAB智能算法30个案例分析 源代码.7z MATLAB求解非线性方程组 fsolve源程序代码.rar MATLAB生成Gif图片程序源代码.rar MATLAB绘制 维维安尼Viviani曲线 源代码程序.rar MATLAB计算粒子速度分布 源程序代码.rar MATLAB设计的简单滤波器程序源代码.rar Matlab语音信号滤波程序.rar matlab通信工程仿真源码(张德丰等编著).zip Matlab金融程序汇总.rar MATLAB霍夫曼Huffman编码译码GUI界面设计 源程序代码.rar mean+shift+tracking.rar MIMO-OFDM(simulinkANDmatlab).rar Motion_Detection.rar music_spectrum.rar OpenSURF_version1c.zip targettrackingusingkalman.rar VC++图像处理与识别实用案例精选.rar zhuantai.rar zxymath_matlab_program.doc 《MATLAB 7.0编程基础基础》源程序.rar 《MATLAB 神经网络43个案例分析》源代码&数据.rar 《MATLAB程序设计教程》源代码-2660.rar 《图像分割中常用的水平集方法的matlab源代码》.zip 【MATLAB GUI设计学习手记】源代码.rar 国外的车牌识别程序.rar 图像分割算法的Matlab源程序matlab.zip 图像压缩.rar 图像处理.rar 图像处理源程序.rar 图像复原matlab程序.rar 图像形态学处理.rar 图像融合+源代码+matlab.rar 图像融合程序代码.rar 图像识别.rar 图像配准算法.rar 图论算法及其MATLAB实现(全书+源代码).zip 基于Matlab的多功能通信信号源仿真.rar 基于仿射变换的数字图象置乱技术 MATLAB源程序代码.rar 完整的QPSK-MSK-QAM-OFDM调制解调m程序.rar 实验心得总结.rar 拉格朗日插值 MATLAB源程序代码.rar 指纹识别的matlab源码.rar 指纹识别的matlab源码.zip 掌握和精通matlab之gui设计.rar 数字信号处理. 理论、算法与实现(胡广书)的MATLAB程序.rar 文字图像识别.rar 牛顿Newton插值 MATLAB源程序代码.rar 现代通信系统(MATLAB版) 源码.zip 用MATLAB对两幅图片进行图像匹配.rar 电滤波效果查看器GUI设计--浙江
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

韩淼燃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值