getopt 函数在头文件 <unistd.h> 中,它可以解析我们传入的命令行参数。 可以实现类似 gcc -o
、rm -rf
等命令。
man 3 getopt
。在线版:http://man.he.net/?topic=getopt§ion=3
#include <unistd.h>
int getopt(int argc, char * const argv[],
const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;
#include <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);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
getopt(): _POSIX_C_SOURCE >= 2 || _XOPEN_SOURCE
getopt_long(), getopt_long_only(): _GNU_SOURCE
使用说明:getopt函数在main()函数中argv携带的参数中进行匹配,以-
为标记。
- 短参数:
-a
、-ab
、-a -b
。其中短参数如果有值的话,可以使用空格分隔,也可以写在一起。例如head -n10
等效head -n10
。 - 长参数 :
--version
。长参数后面如果携带值,可以使用空格分隔,或使用=
链接。
getopt
#include <unistd.h>
int getopt(int argc, char * const argv[],
const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;
对于 getopt() 函数而言,argc和argv参数通常直接从main()的参数直接传递而来。optstring是预处理的可选选项组成的字符串。
optstring:
getopt函数会在argv中进行字符匹配,匹配所使用的参数集合是定义在optstring 中的字母。
- 如果选项字符串里的字母后接着冒号":",则表示还有相关的参数,全域变量optarg 即会指向此额外参数。
- 如果getopt()找不到符合的参数则会印出错信息,并将全域变量optopt 设为"?"字符, 如果不希望getopt()印出错信息,则需要将全域变量opterr 设为0 。
- 如果后面两个冒号,表示后面可以有参数也可以没有参数,但是如果有参数,参数和选项之间不能有空格。
当给定getopt()命令参数的数量 (argc)、指向这些参数的数组 (argv) 和选项字串 (optstring) 后,getopt() 将返回第一个选项,并设置一些全局变量。使用相同的参数再次调用该函数时,它将返回下一个选项,并设置相应的全局变量。如果不再有可识别的选项,将返回 -1,此任务就完成了。
getopt() 所设置的全局变量包括:
- char *optarg
当前选项参数字串(如果有)。 - int optind
argv的当前索引值。当getopt()在while循环中使用时,循环结束后,剩下的字串视为操作数,在argv[optind]至argv[argc-1]中可以找到。 - int opterr
这个变量非零时,getopt()函数为“无效选项”和“缺少参数选项,并输出其错误信息。 - int optopt
当发现无效选项字符之时,getopt()函数或返回’?‘字符,或返回’:'字符,并且optopt包含了所发现的无效选项字符。
需要知道的是,getopts会默认将argv排列,使得选项在前,而非选项的参数放在最后。这样当getopts读取完所有的选项以后,optind会指向非选项的参数。
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main (int argc, char **argv)
{
for(int i = 0; i < argc; ++i)
printf("%s ",argv[i]);
printf("\n");
int c;
while( -1 != (c = getopt (argc, argv, "abc:")))
{
;
}
for(int i = 0; i < argc; ++i)
printf("%s ",argv[i]);
printf("\n");
return 0;
}
示例程序:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main (int argc, char **argv)
{
int aflag = 0; // 如果输入 -a
int bflag = 0; // 如果输入-b
char *cvalue = NULL; // 输入 -c xxxx,保存c选项对应的值
int index;
int c;
opterr = 0; // 错误标记:标记“无效选项”和“缺少参数选项”
while ((c = getopt (argc, argv, "abc:")) != -1)
switch (c)
{
case 'a':
aflag = 1;
break;
case 'b':
bflag = 1;
break;
case 'c':
cvalue = optarg; // 接收c选项的对应的值,如果没有值则下一次getopt返回‘?’
break;
case '?':
if (optopt == 'c')
fprintf (stderr, "Option -%c requires an argument.\n", optopt);
else if (isprint (optopt)) // 检测一个字符是否是可打印字符
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
else // 如果不可打印,输出其ascii值
fprintf (stderr,
"Unknown option character `\\x%x'.\n",
optopt);
return 1;
default:
abort ();
}
// getopt 匹配完毕,输出参数获取情况
printf ("aflag = %d, bflag = %d, cvalue = %s\n",
aflag, bflag, cvalue);
// 将剩余的没有匹配到的参数打印出来
for (index = optind; index < argc; index++)
printf ("Non-option argument %s\n", argv[index]);
return 0;
}
编译并执行:
gcc -Wall -o opt opt.c
./opt
1.无参、-a、-b、-ab、-a -b
2.使用c参数。参数c在代码中设置为 c:
需要对应数值。
3.关于参数对应的值,我们可以将他们紧挨着一起写,也可以使用空格分隔开。同时我们发现如果多次调用 -c 参数,cvalue的值会被覆盖(每次optarg匹配一个值,为了避免被覆盖我们可以使用数据结构将其保存起来,例如 st.push(optacg)
)。
全部执行情况:
% ./opt
aflag = 0, bflag = 0, cvalue = (null)
% ./opt -a -b
aflag = 1, bflag = 1, cvalue = (null)
% ./opt -ab
aflag = 1, bflag = 1, cvalue = (null)
% ./opt -c foo
aflag = 0, bflag = 0, cvalue = foo
% ./opt -cfoo
aflag = 0, bflag = 0, cvalue = foo
% ./opt arg1
aflag = 0, bflag = 0, cvalue = (null)
Non-option argument arg1
% ./opt -a arg1
aflag = 1, bflag = 0, cvalue = (null)
Non-option argument arg1
% ./opt -c foo arg1
aflag = 0, bflag = 0, cvalue = foo
Non-option argument arg1
% ./opt -a -- -b
aflag = 1, bflag = 0, cvalue = (null)
Non-option argument -b
% ./opt -a -
aflag = 1, bflag = 0, cvalue = (null)
Non-option argument -
示例2:写一个程序,要求从命令行传入数据,将其累加结果输出到屏幕上。
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
// 对vector元素求和
int add(vector<int>& vec)
{
int sum = 0;
for(const int & v : vec)
{
sum += v;
}
return sum;
}
int main(int argc, char* argv[])
{
if(argc <= 1){
cout << "please input list-nums. \n ./add 1 2 3 4" << endl;
return 0;
}
vector<int> vec;
for(int i = 0; i < argc; ++i)
{
if(strspn(argv[i], "0123456789") != strlen(argv[i]))
continue; // 只接受数字
int num = stoi(argv[i]); // 将‘1’转为1
vec.push_back(num); // 添加到vec中
}
if(!vec.empty()){
cout << "total sum = " << add(vec) << endl;
}
return 0;
}
如果将它改造成,接收以 -
开头的参数,则代码应该这样写。
#include <iostream>
#include <unistd.h>
#include <cstring>
#include <vector>
using namespace std;
// 帮助字符串,提示该程序的参数
const char* helpstr ={R"(Help:
Example "-l": ./opt -l 1 2 3 4 5 6
Output: total sum = 21
Example "-v": ./opt -l 1,2,3,4,5,6
Output: nums:{ 1, 2, 3, 4, 5, 6 }
total sum = 21
)"
};
// 从命令行接收数据,print 参数表示是否在录入数据时打印到屏幕上
void input(vector<int>& vec, int argc, char* argv[], bool print = false)
{
if(print) cout << "nums:{ " ;
for(int i = optind - 1; i < argc; ++i)
{
if(strspn(argv[i], "0123456789") != strlen(argv[i]))
continue; // 只接受数字
int num = stoi(argv[i]); // 将‘1’转为1
vec.push_back(num); // 添加到vec中
if(print) cout << num << ", "; // 根据参数决定是否打印
}
if(print) cout << "\b\b }" << endl;
}
// 对vector元素求和
int add(vector<int>& vec)
{
int sum = 0;
for(const int & v : vec)
{
sum += v;
}
return sum;
}
int main(int argc, char* argv[])
{
if(argc <= 1){
cout << "please input list-nums. \n ./opt -l 1 2 3 4" << endl;
return 0;
}
vector<int> vec;
bool list_flag = false; // 是否输入了 -l
bool view_flag = false; // 是否输入了 -v
int opt; // 接收命令行 `-` 开头的参数
while ((opt = getopt (argc, argv, "hvl::")) != -1)
switch(opt)
{
case 'h':
cout << helpstr << endl;
break;
case 'v': // 显示输入的元素,即vec的元素
view_flag = true;
break;
case 'l': // 使用 ./opt -l 1 2 3 4 5 6 的方式输入元素
list_flag = true;
break;
case '?':
case ':':
default:
break;
}
if(list_flag){ // 如果使用了 -l 参数,计算求和结果
input(vec, argc, argv, view_flag);
cout << "total sum = " << add(vec) << endl;
}
return 0;
}
-h、-l、-v参数展示
getopt_long
#include <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()类似,只是它也接受以两个减号开始的长选项。(如果程序只接受长选项,那么optstring应该指定为空字符串(""),而不是NULL。)
如果缩写是唯一的,或者与某个已定义的选项精确匹配,则长选项名称可以缩写。长选项可以带有一个形式为 --arg=param
或 --arg param
的参数。
Longopts是指向<getopt.h>中声明的struct option数组的第一个元素的指针
struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
不同字段的含义是:
-
name :是长选项的名称。
-
has_arg :如果选项不带参数,则使用 no_argument(或0);
如果选项需要参数,则使用required_argument(或1);
如果该选项带有可选参数,则使用optional_argument(或2) -
flag :Flag指定如何返回长选项的结果。
如果flag为NULL,那么getopt_long()返回val。(例如,调用程序可能将val设置为等效的短选项字符)。
否则,getopt_long()返回0,并且flag指向一个变量,如果找到该选项则将该变量设置为val,但如果没有找到该选项则保持不变。 -
val : 是要返回的值,或装入由标志所指向的变量中的值
注意:
- 数组的最后一个元素必须填充零。
- 如果longindex不是NULL,它指向一个变量,该变量被设置为long选项相对于longopts的索引。
getopt_long_only()类似于getopt_long(),但是’-‘和"–"可以表示一个长选项。如果以’-’(而不是"–")开头的选项不匹配长选项,但匹配短选项,它将被解析为短选项。
#include <stdio.h> /* for printf */
#include <stdlib.h> /* for exit */
#include <getopt.h>
int main(int argc, char **argv) {
int c;
int digit_optind = 0;
while (1) {
int this_option_optind = optind ? optind : 1;
int option_index = 0;
static struct option long_options[] = {
{"add", required_argument, 0, 0 }, // 需要参数,flag=null,则返回val的值0
{"append", no_argument, 0, 0 }, // 不需要参数,返回val的值0
{"delete", required_argument, 0, 0 }, // 需要参数,返回val的值0
{"verbose", no_argument, 0, 0 }, // 不需要参数,返回val的值0
{"create", required_argument, 0, 'c'}, // 需要参数,返回val的值 ‘c’ 。等效-c选项
{"file", required_argument, 0, 0 }, // 需要参数,返回val的值 0
{0, 0, 0, 0 } // 最后一行元素填充0
};
c = getopt_long(argc, argv, "abc:d:012",
long_options, &option_index);
if (c == -1) // 无参数可匹配了
break;
switch (c) {
case 0: // 本例中接收长参数(-create参数除外)
printf("option %s", long_options[option_index].name);
if (optarg)
printf(" with arg %s", optarg);
printf("\n");
break;
case '0':
case '1':
case '2':
if (digit_optind != 0 && digit_optind != this_option_optind)
printf("digits occur in two different argv-elements.\n");
digit_optind = this_option_optind;
printf("option %c\n", c);
break;
case 'a':
printf("option a\n");
break;
case 'b':
printf("option b\n");
break;
case 'c':
printf("option c with value '%s'\n", optarg);
break;
case 'd':
printf("option d with value '%s'\n", optarg);
break;
case '?':
break;
default:
printf("?? getopt returned character code 0%o ??\n", c);
}
}
if (optind < argc) {
printf("non-option ARGV-elements: ");
while (optind < argc)
printf("%s ", argv[optind++]);
printf("\n");
}
exit(EXIT_SUCCESS);
}
1.接收短参数
2.接收带值的短参
3.长参数映射到 -0 选项。注意,长参数设定为 required_argument 的需要指定一个参数值,使用“=”连接或空格都可以。
4.getopt_long也可以识别长选项的简写,但需要保证简写可以唯一标识某个长选项。例如 --hello 与 --help 这两个长参数无法通过 --he 或 --hel 区分。
示例2:引用自:https://www.jianshu.com/p/e46a37d7b776
#include <unistd.h>
#include <stdio.h>
#include <getopt.h>
int main(int argc, char **argv)
{
int opt, lopt, loidx;
const char *optstring = "ab:c::d:t";
const struct option long_options[] =
{ // 使用--help等参数后,getopt_long()函数返回0,同时将val对应的值赋值到lopt变量中
{"help", no_argument, &lopt, 1}, // getopt_long()返回0,lopt被赋值为1
{"version", no_argument, &lopt, 2}, // ..
{"infile", required_argument, &lopt, 3},
{"outfile", required_argument, &lopt, 4},
{"logfile", optional_argument, &lopt, 5},
{"tttt", no_argument, 0 , 't'}, // 等价于使用 -t 选项
{0, 0, 0, 0}
};
while((opt = getopt_long(argc, argv, optstring, long_options, &loidx)) != -1)
{
if(opt == 0) // long_options[x].flag非空时,getopt_long()函数返回0
opt = lopt; // long_options[x].flag所执行的变量被赋值为long_options[x].val
switch(opt)
{
case 'a':
printf("opt a==%c\n", opt);
break;
case 'b':
printf("opt b==%c, arg: %s\n", opt, optarg);
break;
case 'c':
printf("opt c==%c, arg: %s\n", opt, optarg);
break;
case 'd':
printf("opt d==%c, arg: %s\n", opt, optarg);
break;
case 't':
printf("opt t==%c\n", opt);
printf("\"-t\" equivalent \"--tttt\"\n");
break;
case 1:
printf("opt help==%d\n", opt);
break;
case 2:
printf("opt version==%d\n", opt);
break;
case 3:
printf("opt infile==%d arg: %s\n", opt, optarg);
break;
case 4:
printf("opt outfile==%d arg: %s\n", opt, optarg);
break;
case 5:
printf("opt logfile==%d arg: %s\n", opt, optarg);
break;
default:
printf("error opt %c", opt);
return -1;
}
}
return 0;
}
$ g++ -o test test.cpp
$ ./test --help
opt help==1
$ ./test --help --infile
pt help==1
./test: option '--infile' requires an argument
error opt x
$ ./test --help --infile 123
opt help==1
opt infile==3 arg: 123
1.普通短参数
2.长参数 -tttt 和 与之等效的短参数 -t
3.长参数