Linux命令行参数解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhaobryant/article/details/39085675 在进行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.
目录
相关文章
|
28天前
|
Linux
在Linux中,列出几种常见打包工具并写相应解压缩参数。
在Linux中,列出几种常见打包工具并写相应解压缩参数。
|
19天前
|
图形学 开发者 存储
超越基础教程:深度拆解Unity地形编辑器的每一个隐藏角落,让你的游戏世界既浩瀚无垠又细节满满——从新手到高手的全面技巧升级秘籍
【8月更文挑战第31天】Unity地形编辑器是游戏开发中的重要工具,可快速创建复杂多变的游戏环境。本文通过比较不同地形编辑技术,详细介绍如何利用其功能构建广阔且精细的游戏世界,并提供具体示例代码,展示从基础地形绘制到植被与纹理添加的全过程。通过学习这些技巧,开发者能显著提升游戏画面质量和玩家体验。
52 3
|
19天前
|
存储 Go UED
精通Go语言的命令行参数解析
【8月更文挑战第31天】
17 0
|
26天前
|
安全 数据安全/隐私保护
|
27天前
|
运维 监控 Java
【JVM 调优秘籍】实战指南:JVM 调优参数全解析,让 Java 应用程序性能飙升!
【8月更文挑战第24天】本文通过一个大型在线零售平台的例子,深入探讨了Java虚拟机(JVM)性能调优的关键技术。面对应用响应延迟的问题,文章详细介绍了几种常用的JVM参数调整策略,包括堆内存大小、年轻代配置、垃圾回收器的选择及日志记录等。通过具体实践(如设置`-Xms`, `-Xmx`, `-XX:NewRatio`, `-XX:+UseParallelGC`等),成功降低了高峰期的响应时间,提高了系统的整体性能与稳定性。案例展示了合理配置JVM参数的重要性及其对解决实际问题的有效性。
47 0
|
28天前
|
网络协议 Linux
在Linux中,如何改IP、主机名、DNS?
在Linux中,如何改IP、主机名、DNS?
|
28天前
|
域名解析 网络协议 Linux
在Linux中,我们都知道,dns采用了tcp协议,又采用了udp协议,什么时候采用tcp协议?什么 时候采用udp协议?为什么要这么设计?
在Linux中,我们都知道,dns采用了tcp协议,又采用了udp协议,什么时候采用tcp协议?什么 时候采用udp协议?为什么要这么设计?
|
28天前
|
缓存 网络协议 Linux
在Linux中,当用户在浏览器当中输入⼀个网站,计算机对dns解释经过那些流程?
在Linux中,当用户在浏览器当中输入⼀个网站,计算机对dns解释经过那些流程?
|
28天前
|
存储 安全 Linux
在Linux中,内核调优配置文件名字有哪些?举例几个内核需要优化的参数配置?
在Linux中,内核调优配置文件名字有哪些?举例几个内核需要优化的参数配置?
|
分布式计算 安全 Hadoop
Hadoop2.7实战v1.0之Linux参数调优
 Hadoop2.7实战v1.0之Linux参数调优  1.增大文件描述符nofile(查看当前的lsof |wc -l) 和 用户最大进程nproca>.调整Linux的最大文件打开数和进程数。
858 0

热门文章

最新文章