一、簡介
getopt()函數是一個標准庫調用,可允許您使用直接的 while/switch 語句方便地逐個處理命令行參數和檢測選項(帶或不帶附加的參數)。與其類似的getopt_long()允許在幾乎不進行額外工作的情況下處理更具描述性的長選項,這非常受開發人員的歡迎。
二、示例
1、getopt()
函數原型
getopt(int argc,char *const argv[],const char *optstring)
參數說明
argc和argv一般就將main函數的那兩個參數原樣傳入。
optstring是一段自己規定的選項串,“:”表示該選項必須帶有額外的參數,全域變量optarg會指向此額外參數,“::”標識該額外的參數可選(帶的額外參數必須緊挨著該選項,有些Uinx可能不支持“::”)
全域變量optind指示下一個要讀取的參數在argv中的位置。如果getopt()找不到符合的參數則會印出錯信息,並將全域變量optopt設為“?”字符。如果不希望getopt()印出錯信息,則只要將全域變量opterr設為0即可。
使用示例
#include <stdio.h> #include <unistd.h> int main(int argc,char *argv[]) { int ch; opterr=0; while((ch=getopt(argc,argv,"a:b::cde"))!=-1) { printf("optind:%d\n",optind); printf("optarg:%s\n",optarg); printf("ch:%c\n",ch); switch(ch) { case 'a': printf("option a:'%s'\n",optarg); break; case 'b': printf("option b:'%s'\n",optarg); break; case 'c': printf("option c\n"); break; case 'd': printf("option d\n"); break; case 'e': printf("option e\n"); break; default: printf("other option:%c\n",ch); } printf("optopt+%c\n",optopt); } }
運行
2、getopt_long()
函數原型
int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex)
參數說明
函數中的argc和argv通常直接從main()的兩個參數傳遞而來。optsting是選項參數組成的字符串: 字符串optstring可以下列元素: 1.單個字符,表示選項, 2.單個字符後接一個冒號:表示該選項後必須跟一個參數。參數緊跟在選項後或者以空格隔開。該參數的指針賦給optarg。 3.單個字符後跟兩個冒號,表示該選項後可以有參數也可以沒有參數。如果有參數,參數必須緊跟在選項後不能以空格隔開。該參數的指針賦給optarg。(這個特性是GNU的擴張)。 參數longopts,其實是一個結構的實例: struct option
{ const char *name;//name表示的是長參數名 int has_arg; //has_arg有3個值,no_argument(或者是0),表示該參數後面不跟參數值 // required_argument(或者是1),表示該參數後面一定要跟個參數值 // optional_argument(或者是2),表示該參數後面可以跟,也可以不跟參數值 int *flag; //用來決定,getopt_long()的返回值到底是什麼。如果flag是null(通常情況),則函數會返回與該項option匹配的val值;
//如果flag不是NULL,則將val值賦予flag所指向的內存,並且返回值設置為0
int val; //和flag聯合決定返回值 } 參數longindex,表示當前長參數在longopts中的索引值
使用示例
#include <stdio.h> #include <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 Jay.\n"); break; case 'g': printf("Her name is Afra.\n"); break; case 'l': l_opt_arg = optarg; printf("Our love is %s!\n", l_opt_arg); break; } } return 0; }
運行
三、Apache 命令行處理分析
Apache通過apr_getopt_init函數對命令行結構opt進行初始化
apr_getopt_init(&opt, pcommands, process->argc, process->argv);
apr_getopt_init函數實現在文件:srclib\apr\misc\unix\getopt.c,代碼如下:
APR_DECLARE(apr_status_t) apr_getopt_init(apr_getopt_t **os, apr_pool_t *cont, int argc, const char *const *argv) { void *argv_buff; *os = apr_palloc(cont, sizeof(apr_getopt_t)); (*os)->cont = cont; (*os)->reset = 0; (*os)->errfn = (apr_getopt_err_fn_t*)(fprintf); (*os)->errarg = (void*)(stderr); (*os)->place = EMSG; (*os)->argc = argc; /* The argv parameter must be compatible with main()'s argv, since that's the primary purpose of this function. But people might want to use this function with arrays other than the main argv, and we shouldn't touch the caller's data. So we copy. */ argv_buff = apr_palloc(cont, (argc + 1) * sizeof(const char *)); memcpy(argv_buff, argv, argc * sizeof(const char *)); (*os)->argv = argv_buff; (*os)->argv[argc] = NULL; (*os)->interleave = 0; (*os)->ind = 1; (*os)->skip_start = 1; (*os)->skip_end = 1; return APR_SUCCESS; }
而後調用apr_getopt(opt, AP_SERVER_BASEARGS, &c, &optarg)進行命令行解析處理,代碼如下
while ((rv = apr_getopt(opt, AP_SERVER_BASEARGS, &c, &optarg)) == APR_SUCCESS)
{ char **new; switch (c) { case 'c': new = (char **)apr_array_push(ap_server_post_read_config); *new = apr_pstrdup(pcommands, optarg); break; case 'C': new = (char **)apr_array_push(ap_server_pre_read_config); *new = apr_pstrdup(pcommands, optarg); break; case 'd': def_server_root = optarg; break; case 'D': new = (char **)apr_array_push(ap_server_config_defines); *new = apr_pstrdup(pcommands, optarg); /* Setting -D DUMP_VHOSTS is equivalent to setting -S */ if (strcmp(optarg, "DUMP_VHOSTS") == 0) configtestonly = 1; /* Setting -D DUMP_MODULES is equivalent to setting -M */ if (strcmp(optarg, "DUMP_MODULES") == 0) configtestonly = 1; break; case 'e': if (strcasecmp(optarg, "emerg") == 0) { ap_default_loglevel = APLOG_EMERG; } else if (strcasecmp(optarg, "alert") == 0) { ap_default_loglevel = APLOG_ALERT; } else if (strcasecmp(optarg, "crit") == 0) { ap_default_loglevel = APLOG_CRIT; } else if (strncasecmp(optarg, "err", 3) == 0) { ap_default_loglevel = APLOG_ERR; } else if (strncasecmp(optarg, "warn", 4) == 0) { ap_default_loglevel = APLOG_WARNING; } else if (strcasecmp(optarg, "notice") == 0) { ap_default_loglevel = APLOG_NOTICE; } else if (strcasecmp(optarg, "info") == 0) { ap_default_loglevel = APLOG_INFO; } else if (strcasecmp(optarg, "debug") == 0) { ap_default_loglevel = APLOG_DEBUG; } else { usage(process); } break; case 'E': temp_error_log = apr_pstrdup(process->pool, optarg); break; case 'X': new = (char **)apr_array_push(ap_server_config_defines); *new = "DEBUG"; break; case 'f': confname = optarg; break; case 'v': printf("Server version: %s\n", ap_get_server_description()); printf("Server built: %s\n", ap_get_server_built()); destroy_and_exit_process(process, 0); case 'V': show_compile_settings(); destroy_and_exit_process(process, 0); case 'l': ap_show_modules(); destroy_and_exit_process(process, 0); case 'L': ap_show_directives(); destroy_and_exit_process(process, 0); case 't': configtestonly = 1; break; case 'S': configtestonly = 1; new = (char **)apr_array_push(ap_server_config_defines); *new = "DUMP_VHOSTS"; break; case 'M': configtestonly = 1; new = (char **)apr_array_push(ap_server_config_defines); *new = "DUMP_MODULES"; break; case 'h': case '?': usage(process); } } /* bad cmdline option? then we die */ if (rv != APR_EOF || opt->ind < opt->argc) { usage(process); }
apr_getopt函數實現在文件:srclib\apr\misc\unix\getopt.c,邏輯如下:
1、進行(os->reset || !*os->place)判斷,作用未知
2、通過strchr(opts, os->opt)判斷選項(os->opt)是否合法
3、若選項合法,則通過(*++oli != ':')判斷該選項是否需要額外參數
4、若需要額外參數,則通過optch、optarg返回解析結果
APR_DECLARE(apr_status_t) apr_getopt(apr_getopt_t *os, const char *opts, char *optch, const char **optarg) { const char *oli; /* option letter list index */ if (os->reset || !*os->place) { /* update scanning pointer */ os->reset = 0; if (os->ind >= os->argc || *(os->place = os->argv[os->ind]) != '-') { os->place = EMSG; *optch = os->opt; return (APR_EOF); } if (os->place[1] && *++os->place == '-') { /* found "--" */ ++os->ind; os->place = EMSG; *optch = os->opt; return (APR_EOF); } } /* option letter okay? */ if ((os->opt = (int) *os->place++) == (int) ':' || !(oli = strchr(opts, os->opt))) { /* * if the user didn't specify '-' as an option, * assume it means -1. */ if (os->opt == (int) '-') { *optch = os->opt; return (APR_EOF); } if (!*os->place) ++os->ind; if (os->errfn && *opts != ':') { (os->errfn)(os->errarg, "%s: illegal option -- %c\n", apr_filepath_name_get(*os->argv), os->opt); } *optch = os->opt; return (APR_BADCH); } if (*++oli != ':') { /* don't need argument */ *optarg = NULL; if (!*os->place) ++os->ind; } else { /* need an argument */ if (*os->place) /* no white space */ *optarg = os->place; else if (os->argc <= ++os->ind) { /* no arg */ os->place = EMSG; if (*opts == ':') { *optch = os->opt; return (APR_BADARG); } if (os->errfn) { (os->errfn)(os->errarg, "%s: option requires an argument -- %c\n", apr_filepath_name_get(*os->argv), os->opt); } *optch = os->opt; return (APR_BADCH); } else /* white space */ *optarg = os->argv[os->ind]; os->place = EMSG; ++os->ind; } *optch = os->opt; return APR_SUCCESS; }