• Linux程序之可变参数&&选项那些事!


    一、linux应用程序如何接收参数?

    1. argc、argv

    Linux应用程序执行时,我们往往通过命令行带入参数给程序,比如

    ls /dev/ -l  
    
    • 1

    其中参数 /dev/-l都是作为参数传递给命令 ls

    应用程序又是如何接收这些参数的?

    通常应用程序都是从main函数开始执行,传统的main函数风格如下:

    int main(int argc, char* argv[]) 
    argc:
    程序的命令行参数的数量,用于统计参数数量。 
    argv:
    是一个指向一个字符串数组的指针,数组包含了参数,每个字符串就是一个参数,最后一个元素为0。
    过一般习惯使用多级指针来操作字符串。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    *char argv[]有时候我们也写成char argv

    **argv[]**是一个存放字符类型元素地址的数组。

    因为 C 中是有字符串的概念的:将每个字符存放在 char 数组,最后一个元素为**\0**表示字符串的结束。

    **printf(%s)**就是输出字符串。

    并且一般使用argv指针来访问、处理argv[]数组的内容。

    C语言中,数组就是一个指针加偏移量。

    所以argv则是指向一个指针数组argv[]的指针,不用定义,直接可以用。

    在argv[]数组中存放的的指针指向输入命令的各部分**(调用程序、选项、参数)**。

    2. 举例

    下面我们用一个实例来理解argc和argv

    /*
    * argc: 命令行参数的个数
    * argv: 字符指针数组(指向各个命令行参数的字符指针所构成的数组)
    */
    int main(int argc, char* argv[]) // 接收命令行参数
    {
    	printf("argc=%d\n",argc);
        for (int i = 0; i < argc; i++) {
            printf("argv[%d]: %s\n", i, argv[i]); // 遍历字符指针数组argv
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    执行结果

    peng@ubuntu:~/work$ ./peng arg1 arg2 arg3 
    argc=4
    argv[0]: ./peng
    argv[1]: arg1
    argv[2]: arg2
    argv[3]: arg3
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    参数与argc,argv关系如下:

    二、选项

    1. 选项含义

    linux程序除了上述情况以外,我们还经常会遇到一个使用方法就是选项应用,

    比如:ping命令

    peng@ubuntu:~/work$ ping -h
    Usage: ping [-aAbBdDfhLnOqrRUvV] [-c count] [-i interval] [-I interface]
                [-m mark] [-M pmtudisc_option] [-l preload] [-p pattern] [-Q tos]
                [-s packetsize] [-S sndbuf] [-t ttl] [-T timestamp_option]
                [-w deadline] [-W timeout] [hop1 ...] destination
    
    • 1
    • 2
    • 3
    • 4
    • 5

    参数含义:

    -a:尝试将IP地址解析为主机名。
    -A:使用响应数据包中的附加数据。
    -b:允许ping广播地址。
    -B:不允许ping广播地址。
    -c count:设置要发送的数据包数量。
    -d:使用SO_DEBUG选项。
    -D:不将socket设为分离模式。
    -f:向目标发送一个“强制”数据包。
    -h:显示帮助信息。
    -i interval:设置发送数据包之间的时间间隔。
    -I interface:设置要使用的网络接口。
    -l preload:设置发送的数据包数量。
    -m mark:设置ping数据包的标记。
    -M pmtudisc_option:设置MTU发现选项。
    -n:不要将IP地址解析为主机名。
    -O:启用原始输出。
    -p pattern:设置数据包的模式。
    -Q tos:设置服务类型。
    -r:不使用路由表,直接发送数据包到目标主机。
    -R:启用记录路由。
    -s packetsize:设置数据包的大小。
    -S sndbuf:设置套接字的发送缓冲区大小。
    -t ttl:设置数据包的TTL值。
    -T timestamp_option:设置时间戳选项。
    -U:使用UDP数据包。
    -v:显示详细的ping命令输出。
    -V:显示ping命令的版本信息。
    -w deadline:设置等待响应的时间。
    -W timeout:设置等待响应的超时时间。
    
    destination:指定要ping的目标主机或IP地址。
    
    • 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

    这些 - 开头的都是选项,
    []表示可选的意思

    [-aAbBdDfhLnOqrRUvV]    是无参的选项
    [-c count] [-i interval] [-I interface]
    [-m mark] [-M pmtudisc_option] [-l preload] [-p pattern] [-Q tos]
    [-s packetsize] [-S sndbuf] [-t ttl] [-T timestamp_option]
    [-w deadline] [-W timeout] [hop1 ...]  这些都是有参数的选项
    destination   必须填写的参数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    前辈们利用这点发明了“UNIX 风格”的命令,选项前面加一个横杠-,用于区分选项和参数。

    2. 程序如何区分参数和选项?

    在程序的代码实现中,按照 UNIX 的代码惯例,上来直接跳过第一个,然后判断指针指向的字符串第一个字符是不是-,如果是的,那么进入一个switch判断,用case列出多种支持的情况下,应该执行什么代码。

    例如下面这样就可以判断选项和处理参数:

    int c;
    while (--argc > 0 && (*++argv)[0] == '-' {
    	while (c = *++argv[0] {
    		switch(c){
    		case 'x':
    			...
    			break;
    		case 'n':
    			...
    			break;
    		default:
    			printf("xxx: illegal opyion %c\n", c);
    			...
    			break;
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3. getopt、getopt_long

    事实这么处理选项参数是比较麻烦的,

    linux提供了选项解析的函数:

    // 头文件
    #include
    #include          /*所在头文件 */
    int getopt(intargc, char * const argv[], const char *optstring);
    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);
    extern char *optarg;         /*系统声明的全局变量 */
    extern int optind, opterr, optopt;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    三、getopt

    1. 定义:

    int getopt(int argc, char * const argv[], const char *optstring);
    功能:
    	getopt是用来解析命令行选项参数的,但是只能解析短选项: **-d 100**,不能解析长选项:**--prefix**
    参数
    	argc:
    		main()函数传递过来的参数的个数
    	argv:
    		main()函数传递过来的参数的字符串指针数组
    	optstring:
    		选项字符串,告知 getopt()可以处理哪个选项以及哪个选项需要参数
    返回:
    	如果选项成功找到,返回选项字母;如果所有命令行选项都解析完毕,返回 -1;
    	如果遇到选项字符不在 optstring 中,返回字符 ‘?’;
    	如果遇到丢失参数,那么返回值依赖于 optstring 中第一个字符,
    	如果第一个字符是 ‘:’ 则返回’:‘,否则返回’?'并提示出错误信息。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2. optstring 含义 【重要】

    下边重点举例说明optstring的格式意义:

    char*optstring = “ab:c::;
    单个字符a         表示选项a没有参数            格式:-a即可,不加参数
    单字符加冒号b:     表示选项b有且必须加参数      格式:-b 100-b100,-b=100错
    单字符加2冒号c::   表示选项c可以有,也可以无     格式:-c200,其它格式错误
    
    • 1
    • 2
    • 3
    • 4

    上面这个 optstring 在传入之后,getopt 函数将依次检查命令行是否指定了 -a, -b, -c(这需要多次调用 getopt 函数,直到其返回-1),当检查到上面某一个参数被指定时,函数会返回被指定的参数名称(即该字母)

    系统声明的4个全局变量含义如下:

    optarg —— 指向当前选项参数(如果有)的指针。
    optind —— 再次调用 getopt() 时的下一个 argv指针的索引。
    optopt —— 最后一个未知选项。
    opterr ­—— 如果不希望getopt()打印出错信息,则只要将全域变量opterr设为0即可。
    
    • 1
    • 2
    • 3
    • 4

    3. 实例

    说千道万,不如来一个实例:

    #include
    #include
    #include
    int main(intargc, char *argv[])
    {
        int opt;
        char *string = "a::b:c:d";
        while ((opt = getopt(argc, argv, string))!= -1)
        {  
            printf("opt = %c\t\t", opt);
            printf("optarg = %s\t\t",optarg);
            printf("optind = %d\t\t",optind);
            printf("argv[optind] = %s\n",argv[optind]);
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 正确输入参数,执行结果如下:
    peng@ubuntu:~/work/test$ ./peng -a100 -b 200 -c 300 -d
    opt = a		optarg = 100		optind = 2		argv[optind] = -b
    opt = b		optarg = 200		optind = 4		argv[optind] = -c
    opt = c		optarg = 300		optind = 6		argv[optind] = -d
    opt = d		optarg = (null)		optind = 7		argv[optind] = (null)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    或者

    ork/test$ ./peng -a100 -b200 -c300 -d 
    opt = a		optarg = 100		optind = 2		argv[optind] = -b200
    opt = b		optarg = 200		optind = 3		argv[optind] = -c300
    opt = c		optarg = 300		optind = 4		argv[optind] = -d
    opt = d		optarg = (null)		optind = 5		argv[optind] = (null)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 输入选项参数错误的情况
    peng@ubuntu:~/work/test$ ./peng -a 100 -b 200 -c 300 -d
    opt = a		optarg = (null)		optind = 2		argv[optind] = 100
    opt = b		optarg = 200		optind = 5		argv[optind] = -c
    opt = c		optarg = 300		optind = 7		argv[optind] = -d
    opt = d		optarg = (null)		optind = 8		argv[optind] = (null)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    导致解析错误,第一个 optarg = null,实际输入参数 100,由于格式不正确造成的(可选参数格式固定)

    • 参数丢失,也会导致错误
    peng@ubuntu:~/work/test$ ./peng -a -b 200 -c 
    opt = a		optarg = (null)		optind = 2		argv[optind] = -b
    opt = b		optarg = 200		optind = 4		argv[optind] = -c
    ./peng: option requires an argument -- 'c'
    opt = ?		optarg = (null)		optind = 5		argv[optind] = (null)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    c选项是必须有参数的

    • 命令行选项未定义,-e选项未在optstring中定义,会报错:
    peng@ubuntu:~/work/test$ ./peng -t
    ./peng: invalid option -- 't'
    opt = ?		optarg = (null)		optind = 2		argv[optind] = (null)
    
    • 1
    • 2
    • 3

    四、getopt_long

    1. 定义:

    int getopt_long(int argc, char * const argv[], const char *optstring,
    const struct option *longopts,int *longindex);
    功能:
    	包含 getopt 功能,增加了解析长选项的功能如:--prefix --help
    参数:
    	longopts 
    		指明了长参数的名称和属性
    	longindex 
    		如果longindex非空,它指向的变量将记录当前找到参数符合longopts里的第几个元素的描述,即是 longopts 的下标值
    返回:
    	对于短选项,返回值同 getopt 函数;
    	对于长选项,
    		如果 flag 是 NULL ,返回 val ,否则返回 0 ;
    	对于错误情况返回值同 getopt 函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2. struct option

    struct option {
    	const char  *name;       /* 参数名称 */
    	int          has_arg;    /* 指明是否带有参数 */
    	int          *flag;      /* flag=NULL时,返回value;不为空时,*flag=val,返回0 */
    	int          val;        /* 用于指定函数找到选项的返回值或flag非空时指定*flag的值 */
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    参数has_arg 说明:
    has_arg 指明是否带参数值,其数值可选:

    	no_argument 
    		表明长选项不带参数,如:–name, --help
    	required_argument 
    		表明长选项必须带参数,如:–prefix /root或 --prefix=/root
    	optional_argument 
    		表明长选项的参数是可选的,如:–help或 –prefix=/root,其它都是错误
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3. 实例

    #include
    #include
    #include
    int main(intargc, char *argv[])
    {
        int opt;
        int digit_optind = 0;
        int option_index = 0;
        char *string = "a::b:c:d";
        static struct option long_options[] =
        {  
            {"reqarg", required_argument,NULL, 'r'},
            {"optarg", optional_argument,NULL, 'o'},
            {"noarg",  no_argument,         NULL,'n'},
            {NULL,     0,                      NULL, 0},
        }; 
        while((opt =getopt_long_only(argc,argv,string,long_options,&option_index))!= -1)
        {  
            printf("opt = %c\t\t", opt);
            printf("optarg = %s\t\t",optarg);
            printf("optind = %d\t\t",optind);
            printf("argv[optind] =%s\t\t", argv[optind]);
            printf("option_index = %d\n",option_index);
        }  
    }
    
    • 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
    • 正确执行命令
    peng@ubuntu:~/work/test$ ./long --reqarg 100 --optarg=200 --noarg
    opt = r		optarg = 100		optind = 3		argv[optind] =--optarg=200		option_index = 0
    opt = o		optarg = 200		optind = 4		argv[optind] =--noarg		option_index = 1
    opt = n		optarg = (null)		optind = 5		argv[optind] =(null)		option_index = 2
    
    • 1
    • 2
    • 3
    • 4

    或者

    peng@ubuntu:~/work/test$ ./long –reqarg=100 --optarg=200 --noarg
    opt = o		optarg = 200		optind = 3		argv[optind] =--noarg		option_index = 1
    opt = n		optarg = (null)		optind = 4		argv[optind] =(null)		option_index = 2
    
    • 1
    • 2
    • 3
    • 可选选项可以不给参数
    peng@ubuntu:~/work/test$ ./long --reqarg 100 --optarg --noarg
    opt = r		optarg = 100		optind = 3		argv[optind] =--optarg		option_index = 0
    opt = o		optarg = (null)		optind = 4		argv[optind] =--noarg		option_index = 1
    opt = n		optarg = (null)		optind = 5		argv[optind] =(null)		option_index = 2
    
    • 1
    • 2
    • 3
    • 4
    • 输入长选项错误的情况
    peng@ubuntu:~/work/test$ ./long --reqarg 100 --optarg 200 --noarg
    opt = r		optarg = 100		optind = 3		argv[optind] =--optarg		option_index = 0
    opt = o		optarg = (null)		optind = 4		argv[optind] =200		option_index = 1
    opt = n		optarg = (null)		optind = 6		argv[optind] =(null)		option_index = 2
    
    • 1
    • 2
    • 3
    • 4

    五、getopt_long_only

    getopt_long_only 函数与 getopt_long 函数使用相同的参数表,在功能上基本一致

    只是 getopt_long 只将 --name 当作长参数,但 getopt_long_only 会将 --name 和 -name 两种选项都当作长参数来匹配

    getopt_long_only 如果选项 -name 不能在 longopts 中匹配,但能匹配一个短选项,它就会解析为短选项。

    六、综合实例

    下面这个例子,是一口君从开源项目ifplug提取出来的命令提取小例子,

    大家可以根据自己需要,基于这个框架,定制自己的程序。

    #define _GNU_SOURCE
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define ETHCHECKD_VERSION "1.1"
    
    
    int delay_up = 0;
    char *interface = "eth0";
    
    
    void usage(char *p) {
        if (strrchr(p, '/'))
            p = strchr(p, '/')+1;
    
        printf("%s [options]\n"
               "   -i --iface=IFACE          Specify ethernet interface (%s)\n" 
               "   -d --delay-up=SECS        Specify delay time (%i)\n"
               "   -h --help                 Show this help\n",
               p,
               interface,
               delay_up);
    }
    
    void parse_args(int argc, char *argv[]) {
        static struct option long_options[] = {
    
            {"iface",                required_argument, 0, 'i'},
            {"delay-up",             required_argument, 0, 'd'},
            {"help",                 no_argument, 0, 'h'},
            {"version",              no_argument, 0, 'v'},
            {0, 0, 0, 0}
        };
        int option_index = 0;
        int help = 0, _kill = 0, _check = 0, _version = 0, _suspend = 0, _resume = 0, _info = 0;
        
        for (;;) {
            int c;
            
            if ((c = getopt_long(argc, argv, "i:d:hv", long_options, &option_index)) < 0)
                break;
    
            switch (c) {
                case 'i' :
                    interface = strdup(optarg);
    				printf("interface %s\n",interface);
                    break;
                case 'd':
                    delay_up = atoi(optarg);
    				printf("delay_up %d\n",delay_up);
                    break;
                case 'h':
                    usage(argv[0]);
                    break;
                 case 'v':
                    printf("peng "ETHCHECKD_VERSION"\n");
                    break;
                default:
                    fprintf(stderr, "Unknown parameter.\n");
                    exit(1);
            }
        }
        
    }
    
    static volatile int alarmed = 0;
    
    int main(int argc, char* argv[]) {
    
        parse_args(argc, argv);
        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

    下面是测试结果

    • 短选项
    peng@ubuntu:~/work/test$ ./param -h
    param [options]
       -i --iface=IFACE          Specify ethernet interface (eth0)
       -d --delay-up=SECS        Specify delay time (0)
       -h --help                 Show this help
    peng@ubuntu:~/work/test$ ./param -v
    peng 1.1
    
    peng@ubuntu:~/work/test$ ./param -vh
    peng 1.1
    param [options]
       -i --iface=IFACE          Specify ethernet interface (eth0)
       -d --delay-up=SECS        Specify delay time (0)
       -h --help                 Show this help  
       
    peng@ubuntu:~/work/test$ ./param -i eth3 -d 15
    interface eth3
    delay_up 15 
    
    
    peng@ubuntu:~/work/test$ ./param -i eth3 -d 15 -h
    interface eth3
    delay_up 15
    param [options]
       -i --iface=IFACE          Specify ethernet interface (eth3)
       -d --delay-up=SECS        Specify delay time (15)
       -h --help                 Show this help
    
    • 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
    • 长选项
    peng@ubuntu:~/work/test$ ./param --help
    param [options]
       -i --iface=IFACE          Specify ethernet interface (eth0)
       -d --delay-up=SECS        Specify delay time (0)
       -h --help                 Show this help
       
    peng@ubuntu:~/work/test$ ./param --version
    peng 1.1
    
    peng@ubuntu:~/work/test$ ./param --iface eth3 --delay-up 15
    interface eth3
    delay_up 15
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    talk is cheap!
    test this code!
    
    • 1
    • 2

    快操练起来吧!!!

    更多嵌入式linux资料,后台留言:资料

    也可以加一口君好友

  • 相关阅读:
    医院管理系统数据库,课程设计,SQLserver,纯代码设计
    Spring-Kafka系列(3)—— SpringKafka消费者监听MessageListener
    如何看待人工智能行业发展
    王学岗自定义ViewGroup在哪里获取控件
    第三章:Python中的序列(上)
    Spring boot使用ProGuard实现代码混淆
    云原生爱好者周刊:Dockershim 即将被正式废弃
    英语邮件格式范文
    git上传代码冲突
    Java核心篇,二十三种设计模式(十四),行为型——命令模式
  • 原文地址:https://blog.csdn.net/daocaokafei/article/details/134226715