写了这么多年的Linux下C/C++代码,一直使用getopt_long来解析命令行参数,同时定义一个全局的struct来保存各个命令行参数的值。虽然用得比较“繁琐”,但也安于现状。最近突然发现了Google早在多年前就开源了一个解析命令行参数的“神器”gflags。赶紧来爽一把。
安装
1、去官网下载一个最新的版本(gflags-2.1.1.tar.gz)。
https://github.com/schuhschuh/gflags/archive/v2.1.1.tar.gz
2、现在流行cmake的构建方式,gflags的最新版本也改为使用cmake了。还好我最近也刚刚学习了cmake,算是跟上了潮流。有兴趣的话,可以看 《让cmake显示gcc/g++的编译信息》
[amcool@leooxsoft]$tarxzvf gflags-2.1.1.tar.gz
[amcool@leooxsoft]$cdgflags-2.1.1
[amcool@leooxgflags-2.1.1]$mkdirbuild
[amcool@leooxgflags-2.1.1]$cdbuild/
[amcool@leooxbuild]$cmake..-DCMAKE_INSTALL_PREFIX=/home/amcool/local/gflags-2.1.1
[amcool@leooxbuild]$make
[amcool@leooxbuild]$makeinstall
就是这么简单,安装成功了。值得注意的是,我这里新建了一个build文件夹,即采用“外部构建”的方式。这样编译过程中产生的中间文件(比如.o文件)就都放在build里,不会“污染”gflags源码,做到干干净净。
爽一把
1、既然安装好了,那赶紧来写个简单的代码来爽一把。话不多说,代码才是王道!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// demo.cpp
#include <iostream>
#include <gflags/gflags.h>
usingnamespacestd;
DEFINE_string(confPath,"../conf/setup.ini","program configure file.");
DEFINE_int32(port,9090,"program listen port");
DEFINE_bool(daemon,true,"run daemon mode");
intmain(intargc,char**argv)
{
gflags::ParseCommandLineFlags(&argc,&argv,true);
cout<<"confPath = "<<FLAGS_confPath<<endl;
cout<<"port = "<<FLAGS_port<<endl;
if(FLAGS_daemon){
cout<<"run background ..."<<endl;
}
else{
cout<<"run foreground ..."<<endl;
}
cout<<"good luck and good bye!"<<endl;
gflags::ShutDownCommandLineFlags();
return0;
}
2、很明显,接下来就是要编译了。这里直接用g++写一行命令就可以编译了。但是既然学了cmake,那就“大材小用”一次吧。
project(demo)
cmake_minimum_required(VERSION2.8)
set(CMAKE_VERBOSE_MAKEFILEon)
include_directories("/home/amcool/local/gflags-2.1.1/include")
link_directories("/home/amcool/local/gflags-2.1.1/lib")
add_executable(demodemo.cpp)
target_link_libraries(demogflagspthread)
3、那当然就是编译了
[amcool@leooxdemo]$ls
CMakeLists.txt demo.cpp
[amcool@leooxdemo]$mkdirbuild
[amcool@leooxdemo]$cdbuild
[amcool@leooxbuild]$cmake..
[amcool@leooxbuild]$ls
CMakeCache.txt CMakeFiles cmake_install.cmake Makefile
[amcool@leooxbuild]$make
[amcool@leooxbuild]$ls
CMakeCache.txt CMakeFiles cmake_install.cmake demo Makefile
[amcool@leooxbuild]$
设定命令行参数
1、直接运行,得到的就是我们设定的默认参数。(聪明的你,结合代码一看,就知道参数的默认值是什么了)
[amcool@leooxbuild]$./demo
confPath =../conf/setup.ini
port=9090
run background...
goodluckandgoodbye!
2、设定参数值
i)可以用 –参数名=参数值 或者-参数名=参数值 的方式来设定参数值。
ii)对于bool类型的参数,除了上述方式外,还可以用 –参数名 的方式设定为true(即不带值), 使用–no参数名 的方式设定为false。为了统一,我建议都使用 上面的 第 i)种方法来设定参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[amcool@leooxbuild]$./demo--port=8888--confPath=./setup.ini--daemon=true
confPath =./setup.ini
port=8888
run background...
goodluckandgoodbye!
[amcool@leooxbuild]$./demo-port=8888-confPath=./setup.ini-daemon=false
confPath=./setup.ini
port =8888
runforeground...
good luck andgoodbye!
[amcool@leooxbuild]$./demo-port=8888-confPath=./setup.ini-daemon
confPath =./setup.ini
port=8888
run background...
goodluckandgoodbye!
[amcool@leooxbuild]$./demo-port=8888-confPath=./setup.ini-nodaemon
confPath=./setup.ini
port =8888
runforeground...
good luck andgoodbye!
[amcool@leooxbuild]$
3、从文件读入“命令行”参数
如果我们的程序比较牛逼,配置项非常多,也就是说命令行参数很多,那你每次启动都要一个一个的输入,那岂不是很麻烦?gflags已经帮我们解决了,用 –flagfile=命令行文件 的方式就可以了。你接着往下看,就明白了。param.cmd就是上面说的命令行文件。
[amcool@leooxbuild]$viparam.cmd
--port=8888
--confPath=./setup.ini
--daemon=true
[amcool@leooxbuild]$./demo--flagfile=param.cmd
confPath =./setup.ini
port=8888
run background...
goodluckandgoodbye!
[amcool@leooxbuild]$
怎么样,这样就不怕参数配置错误了吧。保存到文件,每次启动,就很轻松了。
4、从环境变量读入参数值
gflags另外还给我们提供了 –fromenv 和 –tryfromenv 参数,通过这两个参数,我们的程序可以从环境变量中获取到具体的值。两者有什么不一样呢。你看到他们的区别仅仅是有无“try”,聪明的你一定猜到了。
- –fromenv 从环境变量读取参数值 –fromenv=port,confPath 表明要从环境变量读取port,confPath两个参数的值。但是当无法从环境变量中获取到的时候,会报错,同时程序退出。【注意:gflags的变量名是 FLAGS_我们定义的参数名,开篇的代码里,估计细心的你已经发现了】
- –tryfromenv 与–fromenv类似,当参数的没有在环境变量定义时,不退出。
也来一个例子,一看便明了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[amcool@leooxbuild]$./demo--fromenv=port,confPath
ERROR:FLAGS_confPathnotfoundinenvironment
ERROR:FLAGS_portnotfoundinenvironment
[amcool@leooxbuild]$./demo--tryfromenv=port,confPath
confPath=../conf/setup.ini
port =9090
runbackground...
good luck andgoodbye!
[amcool@leooxbuild]$exportFLAGS_confPath=./loveyou.ini
[amcool@leooxbuild]$exportFLAGS_port=36888
[amcool@leooxbuild]$env|grepFLAGS
FLAGS_port=36888
FLAGS_confPath=./loveyou.ini
[amcool@leooxbuild]$
[amcool@leooxbuild]$./demo--fromenv=port,confPath
confPath =./loveyou.ini
port=36888
run background...
goodluckandgoodbye!
[amcool@leooxbuild]$
版本号和帮助信息
我们一般使用程序的时候,都离不开两个参数 –version 和 –help。来看看上面实现的demo能否支持呢?
[amcool@leooxbuild]$./demo--version
demo
[amcool@leooxbuild]$./demo--help
demo:Warning:SetUsageMessage()nevercalled
Flags from/home/thrift/program/gflags/demo/demo.cpp:
-confPath(programconfigurefile.)type:string
default:"../conf/setup.ini"
-daemon(rundaemonmode)type:booldefault:true
-port(programlistenport)type:int32default:9090
哈,help支持了,但是version没支持,而且help信息里面还有waring。没关系,我们可以用 SetVersionString() 和 SetUsageMessage() 方法来满足需求。修改后的代码如下:
【注意:SetVersionString() 和 SetUsageMessage() 一定要在 ParseCommandLineFlags() 之前设定。】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <gflags/gflags.h>
using namespacestd;
DEFINE_string(confPath,"../conf/setup.ini","program configure file.");
DEFINE_int32(port,9090,"program listen port");
DEFINE_bool(daemon,true,"run daemon mode");
int main(intargc,char**argv)
{
gflags::SetVersionString("1.0.0.0");
gflags::SetUsageMessage("Usage : ./demo ");
gflags::ParseCommandLineFlags(&argc,&argv,true);
cout<<"confPath = "<<FLAGS_confPath<<endl;
cout<<"port = "<<FLAGS_port<<endl;
if(FLAGS_daemon){
cout<<"run background ..."<<endl;
}
else{
cout<<"run foreground ..."<<endl;
}
cout<<"good luck and good bye!"<<endl;
gflags::ShutDownCommandLineFlags();
return0;
}
可以来炫一把了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[amcool@leooxbuild]$./demo--version
demo version1.0.0.0
[amcool@leooxbuild]$./demo--help
demo:Usage:./demo
Flags from/home/amcool/program/gflags/demo/demo.cpp:
-confPath(programconfigurefile.)type:string
default:"../conf/setup.ini"
-daemon(rundaemonmode)type:booldefault:true
-port(programlistenport)type:int32default:9090
Flags from/home/amcool/soft/gflags-2.1.1/src/gflags.cc:
-flagfile(loadflagsfrom file)type:stringdefault:""
-fromenv(setflagsfrom theenvironment[use'export FLAGS_flag1=value'])
type:stringdefault:""
-tryfromenv(setflagsfrom theenvironmentifpresent)type:string
default:""
-undefok(comma-separatedlistof flagnamesthat itisokaytospecifyon
thecommandline eveniftheprogram doesnotdefineaflagwith that
name. IMPORTANT:flagsinthislistthat haveargumentsMUST usethe
flag=valueformat)type:stringdefault:""
Flagsfrom/home/amcool/soft/gflags-2.1.1/src/gflags_completions.cc:
-tab_completion_columns(Numberofcolumns touseinoutputfortab
completion)type:int32default:80
-tab_completion_word(Ifnon-empty,HandleCommandLineCompletions()will
hijacktheprocess andattempttodobash-stylecommandline flag
completion onthisvalue.)type:stringdefault:""
Flags from/home/amcool/soft/gflags-2.1.1/src/gflags_reporting.cc:
-help(showhelpon allflags[tip:allflags canhavetwo dashes])
type:booldefault:falsecurrently:true
-helpfull(showhelpon allflags--sameas-help)type:bool
default:false
-helpmatch(showhelpon moduleswhosename containsthe specified substr)
type:stringdefault:""
-helpon(showhelpon themodulesnamed bythisflagvalue)type:string
default:""
-helppackage(showhelpon allmodulesinthemain package)type:bool
default:false
-helpshort(showhelpon onlythemain moduleforthisprogram)type:bool
default:false
-helpxml(produceanxml versionofhelp)type:booldefault:false
-version(showversionandbuildinfo andexit)type:booldefault:false
[amcool@leooxbuild]$
简单讲解如何使用gflags进行编码
有了上面的演示和代码展示,想必大家对gflags有了比较直观的认识。做了这么久的前戏,接下来,终于可以深入了解啦。请看下文《用Google的gflags轻松的编码解析命令行参数》。