命令行参数解析函数 getopt、getopt_long

getopt 函数在头文件 <unistd.h> 中,它可以解析我们传入的命令行参数。 可以实现类似 gcc -orm -rf 等命令。

man 3 getopt 。在线版:http://man.he.net/?topic=getopt&section=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.长参数
在这里插入图片描述

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫RT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值