寫了這麼多年的Linux下C/C++代碼,一直使用getopt_long來解析命令行參數,同時定義一個全局的struct來保存各個命令行參數的值。雖然用得比較“繁瑣”,但也安於現狀。最近突然發現了Google早在多年前就開源了一個解析命令行參數的“神器”gflags。趕緊來爽一把。
安裝
1、去官網下載一個最新的版本(gflags-2.1.1.tar.gz)。
2、現在流行cmake的構建方式,gflags的最新版本也改爲使用cmake了。還好我最近也剛剛學習了cmake,算是跟上了潮流。
[amcool@leoox soft]$ tar xzvf gflags-2.1.1.tar.gz
[amcool@leoox soft]$ cd gflags-2.1.1
[amcool@leoox gflags-2.1.1]$ mkdir build
[amcool@leoox gflags-2.1.1]$ cd build/
[amcool@leoox build]$ cmake .. -DCMAKE_INSTALL_PREFIX=/home/amcool/local/gflags-2.1.1
[amcool@leoox build]$ make
[amcool@leoox build]$ make install
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
就是這麼簡單,安裝成功了。值得注意的是,我這裏新建了一個build文件夾,即採用“外部構建”的方式。這樣編譯過程中產生的中間文件(比如.o文件)就都放在build裏,不會“污染”gflags源碼,做到乾乾淨淨。
爽一把
1、既然安裝好了,那趕緊來寫個簡單的代碼來爽一把。話不多說,代碼纔是王道!
// demo.cpp
#include <iostream>
#include <gflags/gflags.h>
using namespace std;
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(int argc, 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();
return 0;
}
- 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
- 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
2、很明顯,接下來就是要編譯了。這裏直接用g++寫一行命令就可以編譯了。但是既然學了cmake,那就“大材小用”一次吧。
project(demo)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_VERBOSE_MAKEFILE on)
include_directories("/home/amcool/local/gflags-2.1.1/include")
link_directories("/home/amcool/local/gflags-2.1.1/lib")
add_executable(demo demo.cpp)
target_link_libraries(demo gflags pthread)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
3、那當然就是編譯了
[amcool@leoox demo]$ ls
CMakeLists.txt demo.cpp
[amcool@leoox demo]$ mkdir build
[amcool@leoox demo]$ cd build
[amcool@leoox build]$ cmake ..
[amcool@leoox build]$ ls
CMakeCache.txt CMakeFiles cmake_install.cmake Makefile
[amcool@leoox build]$ make
[amcool@leoox build]$ ls
CMakeCache.txt CMakeFiles cmake_install.cmake demo Makefile
[amcool@leoox build]$
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
設定命令行參數
1、直接運行,得到的就是我們設定的默認參數。(聰明的你,結合代碼一看,就知道參數的默認值是什麼了)
[amcool@leoox build]$ ./demo
confPath = ../conf/setup.ini
port = 9090
run background ...
good luck and good bye!
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
2、設定參數值
i)可以用 –參數名=參數值 或者 -參數名=參數值 的方式來設定參數值。
ii)對於bool類型的參數,除了上述方式外,還可以用 –參數名 的方式設定爲true(即不帶值), 使用 –no參數名 的方式設定爲false。爲了統一,我建議都使用 上面的 第 i)種方法來設定參數。
[amcool@leoox build]$ ./demo --port=8888 --confPath=./setup.ini --daemon=true
confPath = ./setup.ini
port = 8888
run background ...
good luck and good bye!
[amcool@leoox build]$ ./demo -port=8888 -confPath=./setup.ini -daemon=false
confPath = ./setup.ini
port = 8888
run foreground ...
good luck and good bye!
[amcool@leoox build]$ ./demo -port=8888 -confPath=./setup.ini -daemon
confPath = ./setup.ini
port = 8888
run background ...
good luck and good bye!
[amcool@leoox build]$ ./demo -port=8888 -confPath=./setup.ini -nodaemon
confPath = ./setup.ini
port = 8888
run foreground ...
good luck and good bye!
[amcool@leoox build]$
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
3、從文件讀入“命令行”參數
如果我們的程序比較牛逼,配置項非常多,也就是說命令行參數很多,那你每次啓動都要一個一個的輸入,那豈不是很麻煩?gflags已經幫我們解決了,用 –flagfile=命令行文件 的方式就可以了。你接着往下看,就明白了。param.cmd就是上面說的命令行文件。
[amcool@leoox build]$ vi param.cmd
--port=8888
--confPath=./setup.ini
--daemon=true
[amcool@leoox build]$ ./demo --flagfile=param.cmd
confPath = ./setup.ini
port = 8888
run background ...
good luck and good bye!
[amcool@leoox build]$
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
4、從環境變量讀入參數值
gflags另外還給我們提供了 –fromenv 和 –tryfromenv 參數,通過這兩個參數,我們的程序可以從環境變量中獲取到具體的值。兩者有什麼不一樣呢。你看到他們的區別僅僅是有無“try”,聰明的你一定猜到了。
–fromenv 從環境變量讀取參數值 –fromenv=port,confPath 表明要從環境變量讀取port,confPath兩個參數的值。但是當無法從環境變量中獲取到的時候,會報錯,同時程序退出。【注意:gflags的變量名是 FLAGS_我們定義的參數名,開篇的代碼裏,估計細心的你已經發現了】
–tryfromenv 與–fromenv類似,當參數的沒有在環境變量定義時,不退出。
也來一個例子,一看便明瞭。
[amcool@leoox build]$ ./demo --fromenv=port,confPath
ERROR: FLAGS_confPath not found in environment
ERROR: FLAGS_port not found in environment
[amcool@leoox build]$ ./demo --tryfromenv=port,confPath
confPath = ../conf/setup.ini
port = 9090
run background ...
good luck and good bye!
[amcool@leoox build]$ export FLAGS_confPath=./loveyou.ini
[amcool@leoox build]$ export FLAGS_port=36888
[amcool@leoox build]$ env | grep FLAGS
FLAGS_port=36888
FLAGS_confPath=./loveyou.ini
[amcool@leoox build]$
[amcool@leoox build]$ ./demo --fromenv=port,confPath
confPath = ./loveyou.ini
port = 36888
run background ...
good luck and good bye!
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
版本號和幫助信息
我們一般使用程序的時候,都離不開兩個參數 –version 和 –help。來看看上面實現的demo能否支持呢?
[amcool@leoox build]$ ./demo --version
demo
[amcool@leoox build]$ ./demo --help
demo: Warning: SetUsageMessage() never called
Flags from /home/thrift/program/gflags/demo/demo.cpp:
-confPath (program configure file.) type: string
default: "../conf/setup.ini"
-daemon (run daemon mode) type: bool default: true
-port (program listen port) type: int32 default: 9090
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
哈,help支持了,但是version沒支持,而且help信息裏面還有waring。沒關係,我們可以用 SetVersionString() 和 SetUsageMessage() 方法來滿足需求。修改後的代碼如下:
【注意:SetVersionString() 和 SetUsageMessage() 一定要在 ParseCommandLineFlags() 之前設定。】
#include <iostream>
#include <gflags/gflags.h>
using namespace std;
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(int argc, 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();
return 0;
}
- 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
- 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
可以來炫一把了:
[amcool@leoox build]$ ./demo --version
demo version 1.0.0.0
[amcool@leoox build]$ ./demo --help
demo: Usage : ./demo
Flags from /home/amcool/program/gflags/demo/demo.cpp:
-confPath (program configure file.) type: string
default: "../conf/setup.ini"
-daemon (run daemon mode) type: bool default: true
-port (program listen port) type: int32 default: 9090
Flags from /home/amcool/soft/gflags-2.1.1/src/gflags.cc:
-flagfile (load flags from file) type: string default: ""
-fromenv (set flags from the environment [use 'export FLAGS_flag1=value'])
type: string default: ""
-tryfromenv (set flags from the environment if present) type: string
default: ""
-undefok (comma-separated list of flag names that it is okay to specify on
the command line even if the program does not define a flag with that
name. IMPORTANT: flags in this list that have arguments MUST use the
flag=value format) type: string default: ""
Flags from /home/amcool/soft/gflags-2.1.1/src/gflags_completions.cc:
-tab_completion_columns (Number of columns to use in output for tab
completion) type: int32 default: 80
-tab_completion_word (If non-empty, HandleCommandLineCompletions() will
hijack the process and attempt to do bash-style command line flag
completion on this value.) type: string default: ""
Flags from /home/amcool/soft/gflags-2.1.1/src/gflags_reporting.cc:
-help (show help on all flags [tip: all flags can have two dashes])
type: bool default: false currently: true
-helpfull (show help on all flags -- same as -help) type: bool
default: false
-helpmatch (show help on modules whose name contains the specified substr)
type: string default: ""
-helpon (show help on the modules named by this flag value) type: string
default: ""
-helppackage (show help on all modules in the main package) type: bool
default: false
-helpshort (show help on only the main module for this program) type: bool
default: false
-helpxml (produce an xml version of help) type: bool default: false
-version (show version and build info and exit) type: bool default: false
[amcool@leoox build]$
- 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
- 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