用Google的gflags優雅的解析命令行參數

寫了這麼多年的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

原文鏈接



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章