Go命令源码文件

本文详细解释了Go语言中命令源码文件的作用、如何编写,以及如何接收和处理参数,包括使用flag包提供的功能和自定义参数类型。

Go命令源码文件

命令源码文件的用途,怎样编写它?

命令源码文件是程序的运行入口,是每个可独立运行的程序必须拥有的。如果一个源码文件声明属于 main 包,并且包含一个无参数声明且无结果声明的 main 函数,那么它就是命令源码文件。
该定义可以被解析为以下两个条件:
属于main包:在Go语言中,main 包具有特别的含义。只有属于 main 包的源文件可以被编译成一个独立的可执行文件。main 包是应用程序的入口点。
无参数声明且无结果声明的 main 函数:这意味着 main 函数不接受任何参数,也不返回任何结果。也就是说,它的声明必须是 func main()。在 Go 中,main 函数不允许有任何输入参数或返回值。它是由 Go 运行时系统直接调用的,而不是由其他代码调用的。

命令源码文件示例:
package main
import "fmt"
func main() {
  fmt.Println("Hello, world!")
}

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

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

package main
import (
	"flag"
	"fmt"
)
var name string
func init() {
	flag.StringVar(&name, "name", "default name", "how to use this.") 
	// 第一个参数用于存储该命令参数值的地址
	// 第二个参数用来指定该命令参数的名称
	// 第三个参数用来指定未追加该命令参数时的默认值
	// 第四个参数是该命令参数的简短说明
}
func main() {
	flag.Parse()
	fmt.Printf("Hello, %s!\n", name)
}

与 flag.StringVar 函数类似的函数是 flag.String。这两个函数的区别是,后者会直接返回一个已经分配好的用于存储命令参数值的地址。

package main
import (
	"flag"
	"fmt"
)
var name *string
func init() {
	flagStringValue := flag.String("name", "default name", "how to use this.")
	name = flagStringValue
}
func main() {
	flag.Parse()
	fmt.Printf("Hello, %s!\n", *name)
}
go run test.go -name="Heihei"
打印结果:
Hello, Heihei
 $ go run demo.go --help
1 Usage of /var/folders/ts/7lg_tl_x2gd_k1lm5g_48c7w0000gn/T/go-build155438482/b001/exe/test
2 -name string
3 how to use this. (default "default name")
4 exit status 2

/var/folders/ts/7lg_tl_x2gd_k1lm5g_48c7w0000gn/T/go-build155438482/b001/exe/test 是 go run 命令构建上述命令源码文件时临时生成的可执行文件的完整路径。如果我们先构建这个命令源码文件再运行生成的可执行文件,像这样:

1 $ go build test.go
2 $ ./test --help
打印结果:
1 Usage of ./test:
2 -name string
3 how to use this. (default "default name")

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

最简单的一种方式就是对变量 flag.Usage 重新赋值flag.Usage 的类型是 func(),即一种无参数声明且无结果声明的函数类型。flag.Usage 变量在声明时就已经被赋值了,所以我们才能够在运行命令 go run demo.go --help 时看到正确的结果。注意:对 flag.Usage 的赋值必须在调用 flag.Parse 函数之前。

 flag.Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage of %s:\n", "question")
    flag.PrintDefaults()
}
$ go run demo.go --help
1 Usage of question:
2 -name string
3 how to use this. (default "default name")
4 exit status 2

在调用 flag 包中的一些函数(比如 StringVar、Parse 等等)的时候,实际上是在调用 flag.CommandLine 变量的对应方法。flag.CommandLine 相当于默认情况下的命令参数容器。所以,通过对 flag.CommandLine 重新赋值,可以更深层次地定制当前命令源码文件的参数使用说明。现在我们把 main 函数体中的那条对 flag.Usage 变量的赋值语句注销掉,修改为如下代码:

package main
import (
	"flag"
	"fmt"
	"os"
)
var name string
func init() {
	flag.CommandLine = flag.NewFlagSet("", flag.ExitOnError)
	flag.CommandLine.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", "question")
		flag.PrintDefaults()
	}
	flag.StringVar(&name, "name", "default name", "how to use it.")
}
func main() {
	flag.Parse()
	fmt.Printf("Hello, %s!\n", name)
}

现在修改为如下代码:

import (
	"flag"
	"fmt"
	"os"
)
var name string
func init() {
	flag.CommandLine = flag.NewFlagSet("", flag.PanicOnError)
	flag.CommandLine.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", "question")
		flag.PrintDefaults()
	}
	flag.StringVar(&name, "name", "default name", "how to use it.")
}
func main() {
	flag.Parse()
	fmt.Printf("Hello, %s!\n", name)
}
--------------------------------------------------------------------------------
PS D:\TestPro> go run test.go --help
Usage of question:
  -name string
        how to use it. (default "default name")
goroutine 1 [running]:
flag.(*FlagSet).Parse(0xc0000740c0, {0xc0000663d0, 0x6a5ab9, 0x60})
        D:/GO/src/flag/flag.go:1021 +0xe8
flag.Parse(...)
        D:/GO/src/flag/flag.go:1036
main.main()
        D:/TestPro/test.go:20 +0x5f
exit status 2

运行 go run test.go --help 命令就会产生另一种输出效果。
这是由于我们在这里传给 flag.NewFlagSet 函数的第二个参数值是 flag.PanicOnError。
flag.PanicOnError 和 flag.ExitOnError 都是预定义在 flag 包中的常量。
flag.ExitOnError 的含义是,告诉命令参数容器,当命令后跟 --help 或者参数设置的不正确的时候,在打印命令参数使用说明后以状态码 2 结束当前程序。状态码2代表用户错误地使用了命令。
flag.PanicOnError 与 flag.ExitOnError 的区别是在最后抛出"运行时恐慌( panic )"。
上述两种情况都会在调用 flag.Parse 函数时被触发。

再进一步,创建一个私有的命令参数容器。
在函数外再添加一个变量声明:var cmdLine = flag.NewFlagSet(“question”, flag.ExitOnError)
然后,把 flag.StringVar 调用替换为 cmdLine.StringVar ,再把 flag.Parse() 替换为 cmdLine.Parse(os.Args[1:])。 其中的 os.Args[1:] 指的就是给定的那些命令参数。
这样完全脱离了 flag.CommandLine。好处是更灵活地定制命令参数容器。
但更重要的是,你的定制完全不会影响到那个全局变量flag.CommandLine。

package main
import (
	"flag"
	"fmt"
	"os"
)
var name string
func init() {
	var cmdLine = flag.NewFlagSet("question", flag.ExitOnError)
	cmdLine.StringVar(&name, "name", "default", "help")
	cmdLine.Parse(os.Args[1:])
}
func main() {
	fmt.Printf("Hello, %s!\n", name)
}

默认情况下,可以让命令源码文件接受哪些类型的参数值?

有布尔类型、整数类型、浮点数类型、字符串类型,以及time.Duration类型。
● string
● int, int64
● uint, uint64
● float64
● bool

可以把自定义的数据类型作为参数值的类型吗?如果可以,怎样做?

可以定义自己的数据类型作为命令行参数值的类型。
为了实现这个功能,需要定义一个新类型,并为这个类型实现 flag.Value 接口。这个接口要求实现以下两个方法:
● String() string:返回参数的字符串表示形式。
● Set(string) error:解析并设置参数的值。

package main
import (
	"flag"
	"fmt"
)
// 定义一个自定义的类型
type myCustomType struct {
	value string
}
// 实现 flag.Value 接口的 String 方法
func (c *myCustomType) String() string {
	return c.value
}
// 实现 flag.Value 接口的 Set 方法
func (c *myCustomType) Set(s string) error {
	// 这里可以加入对值的解析和验证逻辑
	c.value = s
	return nil
}
func main() {
	var customVal myCustomType
	// 使用 flag.Var 函数将自定义类型绑定到命令行参数
	flag.Var(&customVal, "custom", "A custom flag value")
	flag.Parse()
	fmt.Printf("Custom flag value: %s\n", customVal.String())
}
# type Value interface {
# 	String() string
# 	Set(string) error
# }
PS D:\TestPro> go run test.go -custom="test"
Custom flag value: test
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值