• Linux C/C++ 处理命令行参数


    Linux C/C++ 处理命令行参数

    argc 和 argv

    到目前为止,大部分人编写的所有程序都可以用一个命令运行。举个例子,如果我们编译了一个称之为 myprog 的可执行程序,我们能够在 Linux 命令行使用以下命令运行它:

    ./myprog
    
    • 1

    但是,如果你想从命令行向正在运行的程序传递信息,该怎么办?考虑一个更复杂的程序,比如 GCC。要编译 myprog 的可执行文件,我们在命令行输入以下内容:

    gcc -o myprog myprog.c
    
    • 1

    字符串 -o, myprogmyprog.c 都是 gcc 的命令行参数。(从技术上讲,gcc 也是一个参数,我们稍后会看到)

    命令行参数非常有用。毕竟,如果不能向 C 函数传递参数,C 函数就不会很有用了——添加向程序传递参数的功能会使它们更有用。事实上,你在命令行上传递的所有参数最终都作为程序中 main 主函数的参数。

    到目前为止,我们用于 C 程序的框架大致如下:

    #include 
    
    int main() 
    {
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    从现在开始,我们的示例可能看起来更像这样:

    #include 
    
    int main(int argc, char *argv[]) 
    {
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    正如你所看到的,main 函数现在有了参数。变量 argc 代表“参数数量”;argc 包含传递给程序的参数个数。变量 argv 代表“参数向量”。向量(vector)是一维数组,argv 是字符串的一维数组。每个字符串都是传递给程序的参数之一。

    例如命令行:

    gcc -o myprog myprog.c
    
    • 1

    将产生 GCC 内部的以下值:

    argumentvalue
    argc4
    argv[0]gcc
    argv[1]-o
    argv[2]myprog
    argv[3]myprog.c

    如你所见,第一个参数(argv[0])是调用程序的名称,在本示例中它是 gcc。因此,程序将始终至少有一个参数,argc 将始终至少为 1

    以下程序接受任意数量的命令行参数并将其打印出来:

    #include 
    
    int main(int argc, char const *argv[]) {
        printf("argc: %d\n", argc);
        for (int i = 0; i < argc; i++) {
            printf("argv[%d] = %s\n", i, argv[i]);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果你编译你的程序名为 myprog,然后以 ./myprog a b c 的方式调用它,则它会输出以下的打印:

    user@hostname:examples$ ./myprog a b c
    argc: 4
    argv[0] = ./myprog
    argv[1] = a
    argv[2] = b
    argv[3] = c
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    处理命令行参数

    使用自己的程序直接从 argv 向量中提取选项是最容易的,尽管很乏味。

    使用标准 C 选项处理函数 getopt 或同一函数的 GNU 增强版本 getopt_long 稍微不那么乏味,它允许 GNU-style 的长选项(例如,--quiet 而不是 -q)。

    POSIX (Portable Operating System Interface) 标准,建议使用以下命令行参数约定。

    • 如果命令行参数以连字符(-)开头,则为选项。
    • 如果多个选项不带参数,则它们可以组合在连字符后面。因此,-abc-a -b -c 是相同的。
    • 选项名称是单个字母数字字符。
    • 选项可能需要参数。例如,gcc 命令 -o 选项需要输出文件名。
    • 分隔选项及其参数的空格是可选的。因此,-o fname-ofname 是相同的。
    • 选项通常位于非选项参数之前。(事实上,这一个约定并非是强制性)
    • 参数 -- 终止所有选项;后面的所有命令行参数都被视为非选项参数,即使它们以连字符开头。
    • 选项可以以任何顺序出现,甚至多次出现。其含义由应用程序决定。

    此外,GNU 还添加了长选项,如 --help--version 选项。一个长选项以 -- 开头,然后后面跟一串字母数字字符和连字符。选项名称通常为一到三个单词,中间用连字符分隔单词。用户可以缩写选项名称,只要缩写是唯一的。长选项(如 --verbose)通常具有短选项同义词(如 -v)。

    可以为长选项指定参数,如下所示:

    --option-name=value
    
    • 1

    不能在选项名称和等号之间或等号和选项值之间键入空格。

    --option-name= value        # No
    --option-name =value        # No
    --option-anme = value       # No
    
    • 1
    • 2
    • 3

    对于程序的命令行选项,最好遵循 POSIX 指南。最简单的办法就是使用 GNU-style 的 getopt_long 函数解析它们。长名称选项的优点之一是它们可以在不同程序之间保持一致。例如,用户期待任何一个程序都有一个 “help” 的选项,帮助他们了解程序的基本用法。

    接下来我们了解一下 getopt_long 函数的用法:

    #include 
    
    int getopt_long (int argc, char *const argv[], 
                     const char *shortopts,
                     const struct option *longopts, int *longind);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    第 1 个与第 2 个参数 argcargvmain 函数一致。

    第 3 个参数表示短选项字符串,可以是下列元素:

    • 单个字符,表示选项
    • 单个字符后接一个冒号 : 表示该选项后必须跟一个参数,参数紧跟在选项后或者以空格隔开。该参数的指针赋给 optarg
    • 单个字符后跟两个冒号 :: 表示该选项后可以有参数也可以没有参数。如果有参数,参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给 optarg。(这个特性是 GNU 的扩展)

    第 4 个参数指定长选项的结构体 struct option,结构体定义如下:

    struct option
    {
      const char *name;
      int has_arg;
      int *flag;
      int val;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • name: 长选项的名称。

    • has_arg: 表示选项后面是否携带参数,可以使用 no_argument, required_argumentoptional_argument 三个值:

      • no_argument 选项后面不跟参数,如:--version, --help
      • required_argument 选项需要一个参数,输入格式为:--option name--option=name
      • optional_argument 选项采用可选参数,输入格式为:--option--option=name

      在头文件 中这三个值的定义如下:

      /* Names for the values of the 'has_arg' field of 'struct option'.  */
      #define no_argument         0
      #define required_argument   1
      #define optional_argument   2
      
      • 1
      • 2
      • 3
      • 4
    • flag: 该字段有两种含义,空或者非空:

      • 如果为空(NULL),getopt_long 返回 val 值。
      • 如果不为空,则 getopt_long 返回 0,val 赋值给 flag 指针所指向的变量。
    • val: 表示指定函数找到该选项时的返回值。

    第 5 个参数 longind 参数一般赋为 NULL 即可;如果没有设置为 NULL,那么它就指向一个变量,这个变量会被赋值为寻找到的长选项在 longopts 中的索引值,这可以用于错误诊断。

    getopt_long 用法

    以下代码演示了 getopt_long 的基本用法:

    hello.c

    #include       // included for `printf`
    #include      // included for `getopt_long`
    #include      // included for `basename`
    #include      // included for `EXIT_SUCCESS|EXIT_FAILURE`
    
    #define VERSION "1.0.0"
    
    // define option table
    static const struct option longopts[] = {
        {"help", no_argument, NULL, 'h'},
        {"version", no_argument, NULL, 'V'},
        // TODO::
        {"greeting", required_argument, NULL, 'g'},
        {"next-generation", no_argument, NULL, 'n'},
        {"traditional", no_argument, NULL, 't'},
        // ----------
        {NULL, 0, NULL, 0}};
    
    // Print help info.
    static void print_help(const char *progname) {
        printf("Usage: %s [OPTION]...\n", progname);
        printf("Options:\n");
        printf("  -h, --help              display this help\n");
        printf("  -V, --version           display version information\n");
        // TODO::
        printf("  -g, --greeting=TEXT     use TEXT as the greeting message\n");
        printf("  -t, --traditional       use traditional greeting format\n");
        printf("  -n, --next-generation   use next-generation greeting format\n");
        // ----------
    }
    
    static void print_version() {
        printf("%s\n", VERSION);
    }
    
    int main(int argc, char *argv[])
    {
        int optc;
        const char *program_name = basename(argv[0]);
        int lose = 0;
    
        // TODO::
        int t = 0, n = 0;
        const char *greeting = NULL;
        // ----------
    
        while ((optc = getopt_long(argc, argv, "hVg:nt", longopts, NULL)) != -1)
            switch (optc) {
                /* One goal here is having --help and --version exit immediately,
                   per GNU coding standards.  */
                case 'h':
                    print_help(program_name);
                    exit(EXIT_SUCCESS);
                    break;
                case 'V':
                    print_version();
                    exit(EXIT_SUCCESS);
                    break;
                case 'g':
                    greeting = optarg;
                    break;
                case 'n':
                    n = 1;
                    break;
                case 't':
                    t = 1;
                    break;
                default:
                    lose = 1;
                    break;
            }
    
        if (lose || optind < argc) {
            /* Print error message and exit.  */
            if (optind < argc)
                fprintf(stderr, "%s: extra operand: %s\n", program_name,
                        argv[optind]);
            fprintf(stderr, "Try `%s --help' for more information.\n",
                    program_name);
            exit(EXIT_FAILURE);
        }
    
        if (t) {
            printf("hello, world\n");
        } else if (n) {
            printf("+---------------+\n");
            printf("| Hello, world! |\n");
            printf("+---------------+\n");
        } else {
            if (!greeting) greeting = "Hello, world!";
            puts(greeting);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    1. 定义长选项 struct option 结构体数组 longopts

      • 按照需要填充 longopts 数组
      • longopts 的最后一个元素必须是全 0 填充,否则会报段错误
    2. 调用 getopt_long 函数解析命令行参数

    3. 遵循 GNU Coding Standards 规范,提供 --version--help 两个标准选项

    参考资料

    1. argc and argv
    2. Standards for Command Line Interfaces

    欢迎关注我的公众号:飞翔的小黄鸭
    也许会发现不一样的风景


    △ \triangle C/C++读写二进制文件
    ▽ \bigtriangledown Linux C/C++ 单实例进程设计

  • 相关阅读:
    网站部署与上线(2)远程连接云服务器或虚拟机
    数据可视化【原创】vue+arcgis+threejs 实现流光立体墙效果
    微服务--限流
    【继承和多态】
    GPS 数据中的精度因子(DOP)与协方差之间的关系 (参考链接)
    羧基/叠氮/炔烃/DBCO/羟基/生物素修饰的Fe3O4磁性纳米颗粒 COOH-Fe3O4
    零代码编程:用ChatGPT将SRT字幕文件批量转为Word文本文档
    《多功能计算器》 Java程序设计 课程设计
    使用 Sealos 在离线环境中光速安装 K8s 集群
    G1D26-DP presentation&NLP相关
  • 原文地址:https://blog.csdn.net/bluebird_shao/article/details/128067350