getopt、getopt_log详解

本文详细介绍了getopt和getopt_long函数的功能与用法,包括如何解析命令行选项参数,支持短选项和长选项的处理技巧。getopt适用于处理简单的命令行选项,而getopt_long则支持更复杂的长选项格式。

一、getopt

getopt被用来解析命令行选项参数。

#include <unistd.h>
       extern char *optarg;  //选项的参数指针,对应本文最后例子中的-o -l 后面的参数,若是-I -v -h则该参数不可用
       extern int optind,   //下一次调用getopt时,从optind存储的位置处重新开始检查选项。 
       extern int opterr,  //当opterr=0时,getopt不向stderr输出错误信息。
       extern int optopt;  //当命令行选项字符不包括在optstring中或者最后一个选项缺少必要的参数时,该选项存储在optopt中,getopt返回'?’

       int getopt(int argc, char * const argv[], const char *optstring);
 调用一次,返回一个选项。 在命令行选项参数再也检查不到optstring中包含的选项时,返回-1,同时optind储存第一个不包含选项的命令行参数。

首先说一下什么是选项,什么是参数。

字符串optstring可以下列元素,
1.单个字符,表示选项,没有参数,optarg=NULL.
2.单个字符后接一个冒号:表示该选项后必须跟一个参数。参数紧跟在选项后或者以空格隔开。该参数的指针赋给optarg。
3 单个字符后跟两个冒号,表示该选项后必须跟一个参数。参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给optarg。(这个特性是GNU的扩张)。

getopt处理以'-’开头的命令行参数,如optstring="ab:c::d::",命令行为getopt.exe -a -b host -ckeke -d haha 
在这个命令行参数中,-a和-h就是选项元素,去掉'-',a,b,c就是选项。host是b的参数,keke是c的参数。但haha并不是d的参数,因为它们中间有空格隔开。

还要注意的是默认情况下getopt会重新排列命令行参数的顺序,所以到最后所有不包含选项的命令行参数都排到最后。

如果optstring中的字符串以'+'加号开头或者环境变量POSIXLY_CORRE被设置。那么一遇到不包含选项的命令行参数,getopt就会停止,返回-1。

unistd里有个 optind 变量,每次getopt后,这个索引指向argv里当前分析的字符串的下一个索引,因此argv[optind]就能得到下个字符串,通过判断是否以 '-'开头就可。getopt返回-1后,optind指向第一个不包含选项的命令行参数(重排序后的)。

 

二、getopt_long

20 世纪 90 年代,UNIX 应用程序开始支持长选项,即一对短横线、一个描述性选项名称,还可以包含一个使用等号连接到选项的参数。

GNU提供了getopt-long()和getopt-long-only()函数支持长选项的命令行解析,其中,后者的长选项字串是以一个短横线开始的,而非一对短横线。

getopt_long() 是同时支持长选项和短选项的 getopt() 版本。下面是它们的声明:

#i nclude <getopt.h>

int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);

int getopt_long_only(int argc, char * const argv[],const char *optstring,const struct option *longopts, int *longindex);

getopt_long ()的前三个参数与上面的getopt()相同,第4个参数是指向option结构的数组,option结构被称为“长选项表”。longindex参数如果没有设置为NULL,那么它就指向一个变量,这个变量会被赋值为寻找到的长选项在longopts中的索引值,这可以用于错误诊断。

option结构在getopt.h中的声明如下:

struct option{
     const char *name;
     int has_arg;
     int *flag;
     int val;
};

对结构中的各元素解释如下:

const char *name这是选项名,前面没有短横线。譬如"help"、"verbose"之类。int has_arg描述了选项是否有选项参数。如果有,是哪种类型的参数,此时,它的值一定是下表中的一个。 符号常量 数值 含义 no_argument 0 选项没有参数 required_argument 1 选项需要参数 optional_argument 2 选项参数可选 int *flag如果这个指针为NULL,那么getopt_long()返回该结构val字段中的数值。如果该指针不为NULL,getopt_long()会使得它所指向的变量中填入val字段中的数值,并且getopt_long()返回0。如果flag不是NULL,但未发现长选项,那么它所指向的变量的数值不变。int val这个值是发现了长选项时的返回值,或者flag不是NULL时载入*flag中的值。典型情况下,若flag不是NULL,那么val是个真/假值,譬如1 或0;另一方面,如果flag是NULL,那么val通常是字符常量,若长选项与短选项一致,那么该字符常量应该与optstring中出现的这个选项的参数相同。

每个长选项在长选项表中都有一个单独条目,该条目里需要填入正确的数值。数组中最后的元素的值应该全是0。数组不需要排序,getopt_long()会进行线性搜索。但是,根据长名字来排序会使程序员读起来更容易。

以上所说的flag和val的用法看上去有点混乱,但它们很有实用价值,因此有必要搞透彻了。

大部分时候,程序员会根据getopt_long()发现的选项,在选项处理过程中要设置一些标记变量,譬如在使用getopt()时,经常做出如下的程序格式:

int do_name, do_gf_name, do_love; /*标记变量*/
char *b_opt_arg;

while((c = getopt(argc, argv, ":ngl:")) != -1)
{
     switch (c){
     case 'n':
         do_name = 1;
     case 'g':
         do_gf_name = 1;
         break;
         break;
     case 'l':
         b_opt_arg = optarg;
     ……
     }
}

当flag 不为NULL时,getopt_long*()会为你设置标记变量。也就是说上面的代码中,关于选项'n'、'l'的处理,只是设置一些标记,如果 flag不为NULL,时,getopt_long()可以自动为各选项所对应的标记变量设置标记,这样就能够将上面的switch语句中的两种种情况减少到了一种。下面给出一个长选项表以及相应处理代码的例子。

清单5:
#i nclude <stdio.h>
#i nclude <getopt.h>

int do_name, do_gf_name;
char *l_opt_arg;

struct option longopts[] = {
     { "name",         no_argument,             &do_name,         1     },
     { "gf_name",     no_argument,             &do_gf_name,     1     },
     { "love",         required_argument,     NULL,                 'l'     },
     {      0,     0,     0,     0},
};

int main(int argc, char *argv[])
{
     int c;
    
     while((c = getopt_long(argc, argv, ":l:", longopts, NULL)) != -1){
         switch (c){
         case 'l':
             l_opt_arg = optarg;
             printf("Our love is %s!\n", l_opt_arg);
             break;
         case 0:
             printf("getopt_long()设置变量 : do_name = %d\n", do_name);
             printf("getopt_long()设置变量 : do_gf_name = %d\n", do_gf_name);
             break;
         }
     }
     return 0;
}

在进行测试之前,再来回顾一下有关option结构中的指针flag的说明吧。

如果这个指针为NULL,那么getopt_long()返回该结构val字段中的数值。如果该指针不为NULL,getopt_long()会使得它所指向的变量中填入val字段中的数值,并且getopt_long()返回0。如果flag不是NULL,但未发现长选项,那么它所指向的变量的数值不变。

下面测试一下:

$ ./long_opt_demo --name
getopt_long()设置变量 : do_name = 1
getopt_long()设置变量 : do_gf_name = 0

$ ./long_opt_demo --gf_name
getopt_long()设置变量 : do_name = 0
getopt_long()设置变量 : do_gf_name = 1

$ ./long_opt_demo --love forever
Our love is forever!

$ ./long_opt_demo -l forever
Our love is forever!

测试过后,应该有所感触了。关于flag和val的讨论到此为止。下面总结一下get_long()的各种返回值的含义:

返回值 含义

0       getopt_long()设置一个标志,它的值与option结构中的val字段的值一样

1        每碰到一个命令行参数,optarg都会记录它

'?'       无效选项

 ':'        缺少选项参数 'x' 选项字符'x'

 -1       选项解析结束

从实用的角度来说,我们更期望每个长选项都对应一个短选项,这种情况下,在option结构中,只要将flag设置为NULL,并将val设置为长选项所对应的短选项字符即可。譬如上面清单5中的程序,修改如下。

清单6: #i nclude <stdio.h>
#i nclude <getopt.h>

int do_name, do_gf_name;
char *l_opt_arg;

struct option longopts[] = {
     { "name",         no_argument,             NULL,                 'n'     },
     { "gf_name",     no_argument,             NULL,                 'g'     },
     { "love",         required_argument,     NULL,                 'l'     },
     {      0,     0,     0,     0},
};

int main(int argc, char *argv[])
{
     int c;
    
     while((c = getopt_long(argc, argv, ":l:", longopts, NULL)) != -1){
         switch (c){
         case 'n':
             printf("My name is LYR.\n");
             break;
         case 'g':
             printf("Her name is BX.\n");
             break;
         case 'l':
             l_opt_arg = optarg;
             printf("Our love is %s!\n", l_opt_arg);
             break;
         }
     }
     return 0;
}

测试结果如下:

$ ./long_opt_demo --name --gf_name --love forever
My name is LYR.
Her name is BX.
Our love is forever!

$ ./long_opt_demo -ng -l forever
My name is LYR.
Her name is BX.
Our love is forever! 9、在LINUX之外的系统平台上使用GNU getopt()或getopt_long()只要从GNU程序或GNU C Library(GLIBC)的CVS档案文件中copy源文件即可(http://sourceware.org/glibc/)。所需源文件是 getopt.h、getopt.c和getoptl.c,将这些文件包含在你的项目中。另外,你的项目中最好也将COPYING.LIB文件包含进去,因为GNU LGPL(GNU 程序库公共许可证)的内容全部包括在命名为COPYING.LIB 的文件中。

三、结论

程序需要能够快速处理各个选项和参数,且要求不会浪费开发人员的太多时间。在这一点上,无论是GUI(图形用户交互)程序还是CUI(命令行交互)程序,都是其首要任务,其区别仅在于实现方式的不同。GUI通过菜单、对话框之类的图形控件来完成交互,而CUI使用了纯文本的交互方式。在程序开发中,许多测试程序用CUI来完成是首选方案。

getopt() 函数是一个标准库调用,可允许您使用直接的 while/switch 语句方便地逐个处理命令行参数和检测选项(带或不带附加的参数)。与其类似的 getopt_long() 允许在几乎不进行额外工作的情况下处理更具描述性的长选项,这非常受开发人员的欢迎。

附上完整代码:

getopt()例子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
using namespace std;
struct globalArgs_t {
int noIndex; /* -I option */
char *langCode;/* -l option */
const char *outFileName;/* -o option */
FILE *outFile;
int verbosity;/* -v option */
char **inputFiles;/* input files */
int numInputFiles;/* # of input files */
} globalArgs;
static const char *optString = "Il:o:vh?";
/* Display program usage, and exit.
 */
void display_usage( void )
{
puts( "doc2html - convert documents to HTML" );
/* ... */
exit( EXIT_FAILURE );
}
int main( int argc, char *argv[] )
{
int opt = 0;
/* Initialize globalArgs before we get to work. */
globalArgs.noIndex = 0;/* false */
globalArgs.langCode = NULL;
globalArgs.outFileName = NULL;
globalArgs.outFile = NULL;
globalArgs.verbosity = 0;
globalArgs.inputFiles = NULL;
globalArgs.numInputFiles = 0;
while( (opt = getopt( argc, argv, optString )) != -1 ) {
switch( opt ) {
case 'I':
globalArgs.noIndex = 1; //这种后面没有参数的就不可以使用optarg
break;
case 'l':
globalArgs.langCode = optarg;
break;
case 'o':
globalArgs.outFileName = optarg;

printf("%s\n",optarg); //这里输出的就是-o 后面的参数

break;
case 'v':
globalArgs.verbosity++;
break;
case 'h':   /* fall-through is intentional */
case '?': //这里很重要,这是出错的情况
                display_usage();
                break;
default:
                /* You won't actually get here. */
                cout<<"default"<<endl;
                break;
        }
    }
    return EXIT_SUCCESS;
}

getopt_long例子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
const struct option long_opts[]={

//代表输入--help 与 -h 是等价的,一般第三个参数设为NULL,返回最后一个参数

    {"help",no_argument,NULL,'h'},
    {"file",required_argument,NULL,'f'}
};
const char* short_opts = "hf:";
int main(int argc, char* argv[])
{
    int c;
    while((c=getopt_long(argc,argv,short_opts,long_opts,NULL))!=-1)
    {
        printf("%d\n",c);
        switch(c)
        {
            case 'h':
                printf("%s\n","ishelp");
                break;
            case 'f':
                printf("%s\n","isfile");
                printf("%s\n",optarg);
                break;
            case ':':
                printf("%s\n",":d");
                break;
            case '?':
                printf("%s\n","default");
                break;
        }
    }
    return 1;
}

请解释下这个代码:int main(int argc, char *argv[]) { std::cout << "Beerocks Controller Process Start" << std::endl; #ifdef INCLUDE_BREAKPAD breakpad_ExceptionHandler(); #endif init_signals(); // Check for version query first, handle and exit if requested. std::string module_description; std::ofstream versionfile; if (beerocks::version::handle_version_query(argc, argv, module_description)) { return 0; } #ifdef ENABLE_NBAPI auto amxrt = std::make_shared<beerocks::nbapi::Amxrt>(); //init amxrt and parse command line options amxrt_cmd_line_add_option(0, 'k', "kill", no_argument, "kill the process", nullptr); if (auto init = (amxrt->Initialize(argc, argv, handle_cmd_line_arg) != 0)) { std::cout << "Beerocks Agent Process Failed to initalize the amxrt lib." << "amxrt_config_init returned : " << init << " shutting down!" << std::endl; return init; } guarantee = amxrt; (void)guarantee.use_count(); #else int opt; while ((opt = getopt(argc, argv, "kh")) != -1) { switch (opt) { case 'k': s_kill_master = true; break; case 'h': std::cout << "Usage: " << argv[0] << " -k {kill master}" << std::endl; return 1; default: std::cerr << "Unknown option: " << static_cast<char>(optopt) << std::endl; return 1; } } #endif // only kill and exit if (s_kill_master) { return 0; } // Initialize the BPL (Beerocks Platform Library) if (beerocks::bpl::bpl_init() < 0) { LOG(ERROR) << "Failed to initialize BPL!"; return false; } // read master config file std::string master_config_file_path = CONF_FILES_WRITABLE_PATH + std::string(BEEROCKS_CONTROLLER) + ".conf"; //search first in platform-specific default directory beerocks::config_file::sConfigMaster beerocks_master_conf; if (!beerocks::config_file::read_master_config_file(master_config_file_path, beerocks_master_conf)) { master_config_file_path = mapf::utils::get_install_path() + "config/" + std::string(BEEROCKS_CONTROLLER) + ".conf"; // if not found, search in beerocks path if (!beerocks::config_file::read_master_config_file(master_config_file_path, beerocks_master_conf)) { std::cout << "config file '" << master_config_file_path << "' args error." << std::endl; return 1; } } // read slave config file std::string slave_config_file_path = CONF_FILES_WRITABLE_PATH + std::string(BEEROCKS_AGENT) + ".conf"; //search first in platform-specific default directory beerocks::config_file::sConfigSlave beerocks_slave_conf; if (!beerocks::config_file::read_slave_config_file(slave_config_file_path, beerocks_slave_conf)) { slave_config_file_path = mapf::utils::get_install_path() + "config/" + std::string(BEEROCKS_AGENT) + ".conf"; // if not found, search in beerocks path if (!beerocks::config_file::read_slave_config_file(slave_config_file_path, beerocks_slave_conf)) { std::cout << "config file '" << slave_config_file_path << "' args error." << std::endl; return 1; } } std::string base_master_name = std::string(BEEROCKS_CONTROLLER); //kill running master beerocks::os_utils::kill_pid(beerocks_master_conf.temp_path + "pid/", base_master_name); //init logger beerocks::logging logger(base_master_name, beerocks_master_conf.sLog); s_pLogger = &logger; logger.apply_settings(); LOG(INFO) << std::endl << "Running " << base_master_name << " Version " << BEEROCKS_VERSION << " Build date " << BEEROCKS_BUILD_DATE << std::endl << std::endl; beerocks::version::log_version(argc, argv); versionfile.open(beerocks_master_conf.temp_path + "beerocks_master_version"); versionfile << BEEROCKS_VERSION << std::endl << BEEROCKS_REVISION; versionfile.close(); // Redirect stdout / stderr if (logger.get_log_files_enabled()) { beerocks::os_utils::redirect_console_std(beerocks_master_conf.sLog.files_path + base_master_name + "_std.log"); } //write pid file beerocks::os_utils::write_pid_file(beerocks_master_conf.temp_path, base_master_name); std::string pid_file_path = beerocks_master_conf.temp_path + "pid/" + base_master_name; // for file touching // Create application event loop to wait for blocking I/O operations. auto event_loop = std::make_shared<beerocks::EventLoopImpl>(); LOG_IF(!event_loop, FATAL) << "Unable to create event loop!"; // Create timer factory to create instances of timers. auto timer_factory = std::make_shared<beerocks::TimerFactoryImpl>(); LOG_IF(!timer_factory, FATAL) << "Unable to create timer factory!"; // Create timer manager to help using application timers. auto timer_manager = std::make_shared<beerocks::TimerManagerImpl>(timer_factory, event_loop); LOG_IF(!timer_manager, FATAL) << "Unable to create timer manager!"; // Create UDS address where the server socket will listen for incoming connection requests. std::string uds_path = beerocks_slave_conf.temp_path + "/" + std::string(BEEROCKS_CONTROLLER_UDS); auto uds_address = beerocks::net::UdsAddress::create_instance(uds_path); LOG_IF(!uds_address, FATAL) << "Unable to create UDS server address!"; // Create server to exchange CMDU messages with clients connected through a UDS socket auto cmdu_server = beerocks::CmduServerFactory::create_instance(uds_address, event_loop); LOG_IF(!cmdu_server, FATAL) << "Unable to create CMDU server!"; beerocks::net::network_utils::iface_info bridge_info; const auto &bridge_iface = beerocks_slave_conf.bridge_iface; if (beerocks::net::network_utils::get_iface_info(bridge_info, bridge_iface) != 0) { LOG(ERROR) << "Failed reading addresses from the bridge!"; return 0; } #ifdef ENABLE_NBAPI auto on_action_handlers = prplmesh::controller::actions::get_actions_callback_list(); auto events_list = prplmesh::controller::actions::get_events_list(); auto funcs_list = prplmesh::controller::actions::get_func_list(); auto controller_dm_path = mapf::utils::get_install_path() + CONTROLLER_DATAMODEL_PATH; auto amb_dm_obj = std::make_shared<beerocks::nbapi::AmbiorixImpl>( event_loop, on_action_handlers, events_list, funcs_list); LOG_IF(!amb_dm_obj, FATAL) << "Unable to create Ambiorix!"; LOG_IF(!amb_dm_obj->init(controller_dm_path), FATAL) << "Unable to init ambiorix object!"; #else auto amb_dm_obj = std::make_shared<beerocks::nbapi::AmbiorixDummy>(); #endif //ENABLE_NBAPI beerocks::bpl::set_ambiorix_impl_ptr(amb_dm_obj); // fill master configuration son::db::sDbMasterConfig master_conf; fill_master_config(master_conf, beerocks_master_conf); // Set Network.ID to the Data Model if (!amb_dm_obj->set(DATAELEMENTS_ROOT_DM ".Network", "ID", bridge_info.mac)) { LOG(ERROR) << "Failed to add Network.ID, mac: " << bridge_info.mac; return false; } if (!amb_dm_obj->set(DATAELEMENTS_ROOT_DM ".Network", "ControllerID", bridge_info.mac)) { LOG(ERROR) << "Failed to add Network.ControllerID, mac: " << bridge_info.mac; return false; } son::db master_db(master_conf, logger, tlvf::mac_from_string(bridge_info.mac), amb_dm_obj); #ifdef ENABLE_NBAPI prplmesh::controller::actions::g_database = &master_db; #endif // The prplMesh controller needs to be configured with the SSIDs and credentials that have to // be configured on the agents. Even though NBAPI exists to configure this, there is a lot of // existing software out there that doesn't use it. Therefore, prplMesh should also read the // configuration out of the legacy wireless settings. std::list<son::wireless_utils::sBssInfoConf> wireless_settings; if (beerocks::bpl::bpl_cfg_get_wireless_settings(wireless_settings)) { for (const auto &configuration : wireless_settings) { master_db.add_bss_info_configuration(configuration); } } else { LOG(DEBUG) << "failed to read wireless settings"; } #ifdef USE_PRPLMESH_WHM std::shared_ptr<prplmesh::controller::whm::WifiManager> wifi_manager = nullptr; if (master_conf.use_dataelements_vap_configs) { LOG(INFO) << "use dataelements input as vap config"; } else { LOG(INFO) << "legacy behavior: use Device.Wifi."; wifi_manager = std::make_shared<prplmesh::controller::whm::WifiManager>(event_loop, &master_db); LOG_IF(!wifi_manager, FATAL) << "Unable to create controller WifiManager!"; wifi_manager->subscribe_to_bss_info_config_change(); } #endif // diagnostics_thread diagnostics(master_db); // UCC server must be created in certification mode only and if a valid TCP port has been set uint16_t port = master_db.config.ucc_listener_port; std::unique_ptr<beerocks::UccServer> ucc_server; if (master_db.setting_certification_mode() && (port != 0)) { LOG(INFO) << "Certification mode enabled (listening on port " << port << ")"; // Create server to exchange UCC commands and replies with clients connected through the socket ucc_server = beerocks::UccServerFactory::create_instance(port, event_loop); LOG_IF(!ucc_server, FATAL) << "Unable to create UCC server!"; } // Create broker client factory to create broker clients when requested std::string broker_uds_path = beerocks_slave_conf.temp_path + "/" + std::string(BEEROCKS_BROKER_UDS); auto broker_client_factory = beerocks::btl::create_broker_client_factory(broker_uds_path, event_loop); LOG_IF(!broker_client_factory, FATAL) << "Unable to create broker client factory!"; son::Controller controller(master_db, std::move(broker_client_factory), std::move(ucc_server), std::move(cmdu_server), timer_manager, event_loop); if (!amb_dm_obj->set_current_time(DATAELEMENTS_ROOT_DM ".Network")) { return false; }; LOG_IF(!controller.start(), FATAL) << "Unable to start controller!"; auto touch_time_stamp_timeout = std::chrono::steady_clock::now(); while (g_running) { // Handle signals if (s_signal) { handle_signal(); continue; } if (std::chrono::steady_clock::now() > touch_time_stamp_timeout) { beerocks::os_utils::touch_pid_file(pid_file_path); touch_time_stamp_timeout = std::chrono::steady_clock::now() + std::chrono::seconds(beerocks::TOUCH_PID_TIMEOUT_SECONDS); } // Run application event loop and break on error. if (event_loop->run() < 0) { LOG(ERROR) << "Event loop failure!"; break; } } s_pLogger = nullptr; controller.stop(); beerocks::bpl::bpl_close(); return 0; }
最新发布
08-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值