深入浅出Go语言核心编程
上QQ阅读APP看书,第一时间看更新

1.7 编程范例——启动参数的使用

在前面的内容中,我们讲述了开发环境的搭建,以及运行第一个实例程序,本节将通过实例——启动参数的使用来进一步熟悉Go语言程序的基本编写过程。

1.7.1 程序启动的入口函数

在1.4.2节的代码清单1-1中的包名和函数名均为main。对于一个Go程序的入口文件,其包名必须为main。例如,将代码清单1-1中的包名修改为demo:

在修改后的代码中,包名由main修改为了demo,此时无论是在GoLand直接利用菜单命令运行,还是在命令行使用go run命令,均无法正常运行,如下所示。

    $ go run 1-1.go
    package command-line-arguments is not a main package

但是文件1-1.go的编译是可以顺利通过的,这是因为此时的函数的完整引用名称为demo.main(),编译器只是将它视作一个普通函数,而非程序的执行入口。

同样地,函数名main也是一个程序必需的,不能修改为其他名称,否则程序也将无法成功执行。例如,将函数名main修改为demo,并尝试使用go run命令运行,输出结果如下:

    $ go run 1-1.go
    # command-line-arguments
    runtime.main_main·f: function main is undeclared in the main package

因此,我们可以总结出,即使一个最简单的Go语言程序,函数main.main()是其入口的唯一标识,存在且只能存在一个main.main()函数。

1.7.2 获取启动参数

许多编程语言在程序入口函数中有参数定义,用于接收启动时的个性化参数。例如,Java语言的参数接收方式有其固定格式:

    public static void main(String[] args){
    }

其中,参数args是一个String类型的数组。该参数按照顺序接收命令行传递的参数。但是,Go语言使用了完全不同的策略——并没有显式的参数定义(例如前面的main.main()函数的定义中就没有任何参数定义),而是使用固定函数来获取命令行参数。

1.os.Args变量

os.Args是os包中定义的全局变量,其作用类似于Java语言main()方法中的args参数,其源码定义如下:

    var Args []string

该变量是一个string类型的切片。当运行Go程序时,可以增加参数选项,这些参数将会被自动捕获并填充到全局变量os.Args中。如此一来,我们便可以在程序中针对os.Args进行处理。代码清单1-2演示了这一用法。

代码清单1-2 利用os.Args变量获取命令行参数

代码解析:

(1)for i, arg := range os.Args,用于循环处理os.Args中的所有参数,其中变量i代表了每个参数的索引值,arg代表具体参数。

(2)fmt.Printf("参数%d = %s\n", i, arg),用于打印每个参数的索引和具体参数内容。Printf中的f代表Format,意为按一定格式打印;其中的%d和%s均为占位符,分别代表了数值格式和字符串格式。

可以利用如下命令进行测试:

    $ go run 1-2.go a b c

其中,go run 1-2.go为我们常用的运行命令,其后的a、b、c为附加参数,多个附加参数之间利用空格分隔。

该命令执行后,控制台的输出如下:

在输出中,参数0为编译后可执行文件的绝对路径,无论有没有附加参数,参数0都是默认存在的。显式附加的参数a、b、c分别对应了参数1、参数2、参数3。

利用os.Args全局变量可以获得附加参数列表,但是这种形式下,只能通过参数位置进行捕获,这就非常依赖于参数顺序的正确性。

2.os.Flag()函数

除了os.Args外,还可以利用flag.Int()、flag.Bool()、flag.String()等一系列os.Flag()函数来获得参数。与os.Args依赖位置不同,os.Flag()函数可以按参数名获得参数。代码清单1-3演示了这一用法。

代码清单1-3 利用os.Flag()函数获得命令行参数

代码解析:

(1)flag.Int("intVal", 0, "int类型参数"):用于获取名为“intVal”的参数。flag.Int()会将参数视作int类型。其中的0代表如果参数未指定,则使用默认值0。

(2)类似地,flag.Bool("boolVal", false, "bool类型参数")和flag.String("stringVal", "", "string类型参数")分别用于获得名为“boolVal”和“stringVal”的参数,其数据类型分别为布尔型和字符串类型。

(3)flag.Parse()是必需的步骤,用于解析以上参数。

如果在未指定任何参数的情况下运行该代码段,将只输出默认值,如下所示。

    $ go run 1-3.go
    -intVal: 0
    -boolVal: false
    -stringVal:

指定参数的命令也非常简单,其使用格式如下:

    go run main.go -argName1 argVal1 -argName2 argVal2 ...

其中参数名(argName)和参数值(argVal)成对出现,例如-argName1 argVal1 -argName2 argVal2等。需要注意的是参数名之前的中画线(-)不可省略。

可以利用如下命令执行1-3.go,并传递intVal、boolVal和stringVal参数的值:

    $ go run 1-3.go -intVal 15 -boolVal  -stringVal dev

在控制台上的输出如下:

    -intVal: 15
    -boolVal: true
    -stringVal: dev

可以看到,三个参数均已成功获取到指定值。其中稍微有点特殊的是布尔类型的获取,如果我们像其他参数形式一样使用argName+argVal的形式,会发现其后的stringVal无法成功捕获,如下所示。

    $ go run 1-3.go -intVal 15 -boolVal true  -stringVal dev
    -intVal: 15
    -boolVal: true
    -stringVal:

在输出内容中,strintVal的值为空字符,即默认值。这是因为参数-boolVal true虽然在形式上与其他参数保持了一致性,但是对于布尔型参数解析(flag.Bool),单独的“-boolVal”会被识别为一个参数;继续解析时,会尝试将“true”识别为参数名。但是,参数名必须要以“-”开头,所以“true”被识别为非法的参数名,导致解析终止,后续字符串“-stringVal dev”的解析被忽略。

3.命令行参数的最优写法

Go程序指定命令行参数时有以下4种形式可供选择:

    -arg value

--arg value
-arg=value
--arg=value

但是布尔类型比较特殊,只以下支持两种形式:

    -arg

-arg=value

因此,为了统一格式和避免不必要的问题,在使用命令行传递参数时,推荐使用-arg=value。调用代码清单1-3的命令,使用如下形式是最佳选择:

    $ go run 1-3.go -intVal=15 -boolVal=true  -stringVal=dev
    -intVal: 15
    -boolVal: true
    -stringVal: dev