Linux命令行参数解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhaobryant/article/details/39085675

在进行Linux程序开发时,我们最常碰到的一个问题就是:如何设计命令行参数以及如何完成对命令行参数的解析呢?在本文,我们将对这个问题进行详细的阐述和案例分析。

一、库函数详解

当进行命令行参数解析时,主要涉及的库函数包括getopt,getopt_long以及getopt_long_only。其中,主要的参数包括optarg,optind,opterr以及optopt。

现举例说明:

$ myprog -a vv --add -b --file a.txt - -- -e c.txt

Linux中的命令行选项有两种类型,包括短选项和长选项。其中,短选项以'-'作为前导符,长选项以'--'作为前导符。

对于以上命令,可以解释如下:

1. a是短选项,带一个参数vv;
2. add是长选项,无参数;
3. b是短选项,无参数;
4. file是长选项,带一个参数a.txt;
5. -是参数,通常表示标准输入,stdin;
6. --是一个指示符,表明停止扫描参数,其后所有部分都是参数,而不是选项;
7. -e是参数;
8. c.txt是参数。

当执行命令行参数解析时,可以使用一些用于分析命令行参数的函数,它们的原型如下:

#include <unistd.h>

int getopt(int argc, char* const argv[], const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;

#define _GNU_SOURCE
#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函数,其原型如下:

int getopt(int argc, char* const argv[], const char *optstring);

其中,argc表示命令行参数个数,argv表示命令行参数数组,optstring则表示如何分析命令行参数。

关于optstring,有如下几点说明:

  1. 如果选项带参数,该选项后接冒号,比如"a:b",则表示a带参数,而b不带参数;
  2. 如果选项带可选参数,该选项后接两个冒号,比如"a::b",则表示a可能有参数,也可能不带参数;
  3. 如果optstring的起始字符为':',则表示如果指明选项带参数,而实际命令行没有参数时,getopt返回':'而不是'?'(默认情况下返回'?',和无法识别的参数返回一样);
  4. 如果optstring的起始字符为'+',则表示一旦遇到一个无选项参数,马上停止扫描,随后的部分当作参数来解释;
  5. 如果optstring的起始字符为'-',则表示如果遇到无选项参数,则把它当作选项1(不是字符'1')的参数。
  6. 该函数每解析完一个选项,就返回该选项字符。
  7. 如果选项带参数,则参数保存在optarg中。如果选项带可选参数,而实际无参数时,optarg为NULL。
  8. 当遇到一个不在optstring指明的选项时,返回字符'?'。如果在optstring指明某选项带参数而实际没有参数时,返回字符'?'或者字符':',则根据optstring的起始字符而定。这两种情况选项的实际值被保存在optopt中。
  9. 当解析错误时,如果opterr为1则自动打印一条错误消息(默认),否则不打印。
  10. 当解析完成时,函数返回-1。
  11. 每当解析完一个argv,optind(选项索引)就会递增。如果遇到无选项参数,getopt默认会把该参数调后一位,再解析下一个参数。如果解析完成后还有无选项的参数,则optind指示的是第一个无选项参数在argv中的索引。

对于可以同时分析长选项和短选项的getopt_long函数,其原型如下:

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

相较于getopt函数,它同时能够接收长选项。在接收长选项之前,必须定义一个option结构体数组longopts,用于存储希望解析的长选项信息。

对于option结构体,其定义如下:

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

对于结构体的每个成员,含义如下:

  1. name表示长选项的名称。
  2. has_arg表示该选项是否带参数,包括no_argument,required_argumen以及optional_argument。
  3. flag表示长选项如何返回,如果flag为NULL,则getopt_long返回val。否则返回0,flag指向一个值为val的变量。如果该长选项没有发现,flag保持不变。
  4. val表示长选项的返回值,或需要被加载到flag所指向的变量中。

另外,对于longopts数组,最后一个元素必须被全部填充为0,即{0, 0, 0, 0}。同时,getopt_long函数的最后一个参数longindex在函数返回时指向被搜索到的选项在longopts数组中的下标。longindex可以为NULL,表示不需要返回该值。

getopt_long_only类似于getopt_long,但是它把'-'开头的选项当作长选项来处理。如果该选项与长选项不匹配,而与短选项匹配,则可以作为短选项解析。在短选项找到的时候,getopt_long和getopt_long_only的表现和getopt一样。如果长选项找到了,如果flag为 NULL,返回val,否则返回0。错误情况的处理和getopt一样,只是返回'?'时还可能是别的情况引起的:选项含糊不明确或者无关参数。

二、举例说明

代码如下:

#include <stdio.h>
#include <stdlib.h>
#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 },
            {"append",  no_argument,       0,  0 },
            {"delete",  required_argument, 0,  0 },
            {"verbose", no_argument,       0,  0 },
            {"create",  required_argument, 0, 'c'},
            {"file",    required_argument, 0,  0 },
            {0,         0,                 0,  0 }
        };

        c = getopt_long(argc, argv, "a:bc:d:012",
                long_options, &option_index);
        if (c == -1) {
            printf("arguments parse work completed.\n");
            break;
        }

        switch (c) {
        case 0:
            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-element.\n");
            }
            digit_optind = this_option_optind;
            printf("option %c\n", c);
            break;

        case 'a':
            printf("option a with value '%s'\n", optarg);
            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);
}

测试如下:

zjl@ubuntu:~/workset/cmdparse$ ./myprog -a vv --add -b --file a.txt --delete d
option a with value 'vv'
option add with arg -b
option file with arg a.txt
option delete with arg d
arguments parse work completed.
上一篇:你可能不知道的 css 内容块


下一篇:一个老程序员对自己当前编程技术处在哪个水平的反思