在软件开发中,程序支持命令行的输入,对于代码调试、问题定位起着非常重要的作用,可以大大提升编码的效率。
这篇文章就介绍python、golang、c++三种语言如何实现对命令行的支持。
1、python语言的实现
在python中,最常见的实现命令行的方法是使用内置的【argparse】模块。
导入【argparse】模块:
import argparse
创建argparse对象:
parser = argparse.ArgumentParser()
注册命令行参数:
# 添加参数
parser.add_argument('echo', help='位置参数')
parser.add_argument('-v', '--verbose', help='show version')
parser.add_argument('-b', '--batch', type=int, default=100, help='set batch size')
在使用add_argument函数设置命令行参数时,参数可以有两种形式:
位置参数:比如上面代码的【echo】,这些参数不带【--】
前缀,
且都为必须,上面代码示例中位置参数在第一个,程序运行时,必须在第一个位置设定其值,否则将获取不到该参数的值。
选项参数:这些参数通常都带有【--】或者【-】前缀,没有顺序要求,并且可以为选项参数设定默认值。
对于add_argument函数几个重要的参数:
parser.add_argument(name or flags...[, action][, nargs][, const][, default][, type][,choices][, required][, help][, metavar][, dest])
action:可设定四个值:store、store_true、store_false、append,其中store是默认值,即获取参数值,store_true、store_false即带上这个参数时是true或者false,append为可设定多个值,并将参数值放在list当中。
nargs:指定参数的个数,分别代表如下含义:
'+':表示选项参数可以接受一个或多个参数值。多个参数值将被解析为一个列表。例如,-m 1 4 8 将解析为 args.milestones = [1, 4, 8]。
'*':表示选项参数可以接受零个或多个参数值。多个参数值将被解析为一个列表。例如,-m 15 30 45 将解析为 args.milestones = [15, 30, 45],而不提供 -m 参数将解析为 args.milestones = []。
int:表示选项参数为固定数量的参数值。例如,nargs=2 表示选项参数接受两个参数值。例如,-m 15 30 将解析为 args.milestones = [15, 30]。
解析命令行:
# 解析参数
args = parser.parse_args()
通过命令行参数名称获取参数值:
print("Batch size:", args.batch)
一个完整的代码示例:
import argparse
if __name__ == "__main__":
parser = argparse.ArgumentParser()
# 添加参数
parser.add_argument('pos', help='位置参数')
parser.add_argument('-v', '--verbose', help='enable verbose mode',action="store_true")
parser.add_argument('-b', '--batch', type=int, default=100, help='set batch size')
parser.add_argument('-m', '--milestones', nargs='+', required=True, default=15)
# 解析参数
args = parser.parse_args()
# 使用参数
if args.verbose:
print("verbose mode is on",args.verbose)
print("pos参数:", args.pos)
print("batch size:", args.batch)
print("milestones:", args.milestones)
2、golang的实现
golang的命令行实现非常的简单,主要使用flag包,设定整型、字符串、bool、时长类型等各种参数,比如增加一个-name参数:
name := flag.String("name", "World", "a name to say hello")
或者写成:
flag.StringVar(&name, "name", "World", "a name to say hello")
两种写法实现的功能完全一样,都是增加了一个字符串参数name,默认值world,参数的帮助信息为a name to say hello。
除了可以设定字符串类型命令参数,也可以设定整型、时长类型、浮点等等类型的参数,一个完整的例子如下:
package main
import (
"flag"
"fmt"
)
func main() {
var enable bool
name := flag.String("name", "World", "a name to say hello")
age := flag.Int("age", 35, "age in years")
flag.BoolVar(&enable, "e", false, "shorthand for enable")
du := flag.Duration("d", 0, "shorthand for duration")
flag.Parse()
fmt.Println("Hello", *name)
fmt.Println("Age", *age)
fmt.Println("Enable", enable)
fmt.Println("Duration", *du)
}
3、c++的实现
在c++中,支持短选项参数以及长选项参数需要使用不同的函数(也可以使用一个函数),短选项参数就是-b、-v,长选项参数--prefix、--version类似这种。
首先对于短选项命令行参数,我们使用函数:
int getopt(int argc, char * const argv[], const char *optstring);
argc:main()函数传递过来的参数的个数
argv:main()函数传递过来的参数的字符串指针数组
optstring:选项字符串,告知 getopt()可以处理哪个选项以及哪个选项需要参数
重点说明下optstring字段:
char*optstring = “ab:c::”;
单个字符a 表示选项a没有参数 格式:-a即可,不加参数
单字符加冒号b: 表示选项b有且必须加参数 格式:-b 100或-b100,但-b=100错
单字符加2个冒号c:: 表示选项c可以有,也可以无 格式:-c200,其它格式错误
对于函数的返回值:
如果选项成功找到,返回选项字母;如果所有命令行选项都解析完毕,返回 -1,如果没有找到对应命令行参数,返回【?】
几个全局全量:
optarg —— 指向当前选项参数(如果有)的指针。
optind —— 再次调用 getopt() 时的下一个 argv指针的索引。
optopt —— 最后一个未知选项。
opterr —— 如果不希望getopt()打印出错信息,则只要将全域变量opterr设为0即可。
一般情况下,我们只用到前两个。
#include <iostream>
#include <thread>
#include<getopt.h>
int main(int argc, char *argv[])
{
// unsigned int n = std::thread::hardware_concurrency();
// std::cout << " concurrent threads are supported = " << n << std::endl;
int opt;
std::string str = "a::b:c:d";
while ((opt = getopt(argc, argv, str.data()))!= -1) {
printf("opt = %c\t\t", opt);
printf("optarg = %s\t\t",optarg);
printf("optind = %d\t\t",optind);
printf("argv[optind] = %s\n",argv[optind]);
}
std::cout << "res:" << opt << std::endl;
}
对于长选项参数,一般使用如下函数:
int getopt_long(int argc, char * const argv[], const char *optstring,
const struct option *longopts, int*longindex);
首先这个函数包含了上面【getopt】函数的全部功能,即支持短选项,又支持长选项参数,对于c++的命令行实现,完全可以用这一个函数。
函数参数说明如下:
optstring 短选项命令行设置,和getopt一致。
longopts 指明了长参数的名称和属性
longindex 如果longindex非空,它指向的变量将记录当前找到参数符合longopts里的第几个元素的描述,即是longopts的下标值
struct option结构体:
struct option {
const char *name; /* 参数名称 */
int has_arg; /* 指明是否带有参数 */
int *flag;
int val;
};
int *flag 和 int val:这两个字段用于控制 getopt_long 的返回值和行为。如果 flag 不是 NULL,则 getopt_long 会将 val 的值赋给 *flag,并返回 0,这通常用于不需要返回选项字符的“标志”选项。如果 flag 是 NULL,则 getopt_long 返回 val 的值(对于短选项,这通常是选项字符;对于长选项,这是自定义的值)。
has_arg有三个值可选:
no_argument 表明长选项不带参数,如:--name, --help
required_argument 表明长选项必须带参数,如:--prefix /root或 --prefix=/root
optional_argument 表明长选项的参数是可选的,如:--help或 –prefix=/root,其它都是错误
使用示例:
#include <iostream>
#include <thread>
#include <getopt.h>
int main(int argc, char *argv[])
{
unsigned int n = std::thread::hardware_concurrency();
std::cout << " concurrent threads are supported = " << n << std::endl;
int opt;
std::string str = "a::b:c:d";
static struct option long_options[] =
{
{"reqarg", required_argument,NULL, 'r'},
{"optarg", optional_argument,NULL, 'o'},
{"noarg", no_argument, NULL,'n'},
{NULL, 0, NULL, 0},
};
int option_index;
while((opt = getopt_long(argc,argv,str.data(),long_options,&option_index))!= -1)
{
printf("opt = %c\t\t", opt);
printf("optarg = %s\t\t",optarg);
printf("optind = %d\t\t",optind);
printf("argv[optind] =%s\t\t", argv[optind]);
printf("option_index = %d\n",option_index);
}
}
编译上述代码并运行:
好了,三种语言的命令行支持就全部实现了,需要的同学可以直接拿去用,全部代码都在这里:
https://github.com/liupengh3c/career/tree/main/cmd
欢迎小伙伴们关注、交流,也非常欢迎大家大家关注公众号微信沟通,知无不言~~~~~~~~~~。