欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Go语言 命令行解析(一)

程序员文章站 2022-03-25 17:00:20
命令行启动服务的方式,在后端使用非常广泛,如果有写过C语言的同学相信不难理解这一点!在C语言中,我们可以根据argc和argv来获取和解析命令行的参数,从而通过不同的参数调取不同的方法,同时也可以用Usage来打印帮助信息了。 那么开始今天的话题之前,我们回顾一下在C语言中是如何解析传递的参数的。 ......

命令行启动服务的方式,在后端使用非常广泛,如果有写过c语言的同学相信不难理解这一点!在c语言中,我们可以根据argc和argv来获取和解析命令行的参数,从而通过不同的参数调取不同的方法,同时也可以用usage来打印帮助信息了。

那么开始今天的话题之前,我们回顾一下在c语言中是如何解析传递的参数的。

示例代码:

#include <stdio.h>
#include <stdlib.h>

// argc 为int型
// argv 为char指针数组,元素个数是argc,存放的是指向每一个参数的指针
int main(int argc, char *argv[])
{

    printf("命令行参数个数: %d\n", argc);
    printf("执行程序的名称:%s\n", argv[0]);

    int i = 1; // 从下标1开始获取,因为0代表的是程序名称

    while(i < argc)
    {
        // 循环打印每个命令行的参数
        printf("%s\n", argv[i]);
        i++;
    }

    return 0;
}
Go语言 命令行解析(一)

编译运行:

#gcc c_cli.c -o c_cli
./c_cli /usr/local/service/config /usr/local/service/log/service.log                   
命令行参数个数: 3
执行程序的名称:./c_cli
/usr/local/service/config
/usr/local/service/log/service.log
Go语言 命令行解析(一)

看完上面的例子,其实我们可以发现,上例中是c语言自带的参数解析,对于简单的参数构成还是可以使用的。下面我们再看一下go语言os标准库的实现。

示例代码:

package main

import (
	"fmt"
	"os"
)

func main() {

	var args = os.args
	fmt.println(args)

	return
}
Go语言 命令行解析(一)

编译执行:

#go build go_flag.go
#./go_flag /usr/local/service/config /usr/local/service/log/service.log
[./go_flag /usr/local/service/config /usr/local/service/log/service.log]
Go语言 命令行解析(一)

上例中,我们可以看到os.args返回一个数组,数组里面是我们命令行执行时,所传递的参数和程序名称。os自带的参数获取,对于简单的参数来说还能使用,如果参数复杂的情况下,那么解析起来就比较费劲的。这个时候,我们可以选择go语言的flag标准库来帮我们处理命令行解析工作。

flag包:

是go语言提供的一个标准库,能够较为方便和灵活的解析命令行传递的参数。

flag有两种方式:

1、flag.type,其中type可以是:int、string、bool,float等类型,返回指针类型。

var port = flag.int("port", 0, "相关描述")
Go语言 命令行解析(一)

参数1:flag的名称

参数2:flag的值,上例中默认值是0

参数3:flag的描述

2、flag.typevar,将类型绑定到一个变量上。

var port int
flag.intvar(&port, "port", 0, "相关描述")
Go语言 命令行解析(一)

参数1:flag的值

参数2:flag的名称

参数3:flag的值,上例中默认值是0

参数4:flag的描述

示例代码:

package main

import (
	"flag"
	"fmt"
	"os"
)

var (
	// 定义一个tcp端口号
	tcp_port int

	// flag.type(name为flag的名字,value是flag的值,usage是flag的提示信息)
	port    = flag.int("port", 0, "服务端口设置参数为:-port=80")
	config  = flag.string("config", "", "配置文件配置参数为:-config=/usr/local/service/config")
	logfile = flag.string("logfile", "", "日志文件配置参数为:-logfile=/usr/local/service/log/service.log")
	// flag.type返回的是指针类型,所以获取值为"*变量"

)

func init() {
	flag.intvar(&tcp_port, "tcp_port", 0, "tcp服务端口描述: -tcp_port=2001")
	flag.usage = func() {
		_, _ = fmt.fprint(os.stderr,
			"cli : go_flag -port=8080 -config=/usr/local/service/config "+
				"-logfile=/usr/local/service/log/service.log -tcp_port=2001\n")
		flag.printdefaults()
	}

	flag.parse()
}

func main() {

	if *port <= 0 {
		flag.printdefaults()
		os.exit(1)
	}

	if tcp_port <= 0 {
		flag.printdefaults()
		os.exit(1)
	}

	if *config == "" {
		flag.printdefaults()
		os.exit(2)
	}

	if *logfile == "" {
		flag.printdefaults()
		os.exit(3)
	}

	fmt.printf("service tcp port : %d \n", tcp_port)
	fmt.printf("service port : %d \n", *port)
	fmt.printf("service config : %s \n", *config)
	fmt.printf("service logfile : %s \n", *logfile)
	return
}
Go语言 命令行解析(一)

运行结果:

#go build go_flag.go
#./go_flag 
  -config string
        配置文件配置参数为:-config=/usr/local/service/config
  -logfile string
        日志文件配置参数为:-logfile=/usr/local/service/log/service.log
  -port int
        服务端口设置参数为:-port=80
  -tcp_port int
        tcp服务端口描述: -tcp_port=2001

#./go_flag -port 8090 -config=/usr/local/service/config -logfile= /usr/local/service/log/service.log -tcp_port=2001
service tcp port : 2001 
service port : 8090 
service config : /usr/local/service/config 
service logfile : /usr/local/service/log/service.log 
Go语言 命令行解析(一)

查看帮助:

#./go_flag -h
cli : go_flag -port=8080 -config=/usr/local/service/config -logfile=/usr/local/service/log/service.log
  -config string
        配置文件配置参数为:-config=/usr/local/service/config
  -logfile string
        日志文件配置参数为:-logfile=/usr/local/service/log/service.log
  -port int
        服务端口设置参数为:-port=80
  -tcp_port int
        tcp服务端口描述: -tcp_port=2001
Go语言 命令行解析(一)

flag语法:

  • -flag 只支持bool参数
  • -flag=p 
  • -flag p bool类型不能使用,当p为false时会引起歧义

flag解析:

parseone()函数来处理-flag=value的,如果处理成功后,会将flag存储到flagset.actucal map[string]*flag中。

flag.parse()函数来解析命令行中的参数中定义的flag,该方法遇到第一个非flag的命令方法就中止。

1、non-flag的时候,会终止解析工作。

Go语言 命令行解析(一)Go语言 命令行解析(一)

2、连续两个“-”的时候,会终止解析工作。
Go语言 命令行解析(一)Go语言 命令行解析(一)

然后,再看我们传递错误的参数的返回情况。

#./go_flag -a=b
flag provided but not defined: -a
cli : go_flag -port=8080 -config=/usr/local/service/config -logfile=/usr/local/service/log/service.log
  -config string
        配置文件配置参数为:-config=/usr/local/service/config
  -logfile string
        日志文件配置参数为:-logfile=/usr/local/service/log/service.log
  -port int
        服务端口设置参数为:-port=80
  -tcp_port int
        tcp服务端口描述: -tcp_port=2001
Go语言 命令行解析(一)

跟踪到代码中可以看到如图:

Go语言 命令行解析(一)Go语言 命令行解析(一)

从上例可见,其实flag并没有非常强大,下一章节会介绍一个更强大的命令行解析工具。

总结:

  1. os标准库提供的解析方法,能够解析简单的命令行参数。
  2. flag能够解析约定好的常规按照-flag传递的命令行参数,有帮助信息。
  3. os和flag还不能够解析复杂结构的启动参数。

喜欢的话,可以关注公众号