程序参数
当我们用C语言写好一个程序并成功编译出可执行的二进制文件之后,可以通过终端进行调用。并且在调用的时候,通常会传入一些参数,这些传入的参数会被存储到一个字符串数组中,并且该数组会被作为main()
函数的参数,以便程序对这些参数进行处理。例如
int main(int argc, char *argv[]){
for(int i = 0; i < argc; i++){
printf("%s\n", argv[i]);
}
return 0;
}
这个main
函数中有两个参数,第一个argc
表示程序被调用时接受参数的个数,第二个argv
用于存储传入的参数。
我们使用Linux
系统中一些命令的时候通常会用-
或--
开头的字符来表示选项,例如ls -al
,这里的-al
表示的就是程序的选项。这是一种规范,我们开发程序的时候最好遵守这样的规范以让我们的程序更容易被人接受。那么为了遵守这样的规范,我们可以手动对程序接受的参数进行检查,如果参数以-
或--
开头就说明该参数表示一个选项。
但是在Linux
中有一种更好的选择,就是使用Linux
系统提供的getopt()
和getopt_long()
函数。
getopt()
该函数用来处理调用程序传入的参数,处理的方式遵循标准的Linux
风格选项风格,例如ls -al
,以-
开头的字符表示选项,选项后面可以跟一个字符串表示该选项对应的参数。
函数的声明以及函数要用到的全局变量的定义都在头文件<unistd.h>
中。如下所示:
int getopt(int argc, char * const argv[], const char * optstring);
extern char * optarg;
extern int optind, opterr, optopt;
下面是对该函数参数和全局变量的说明:
argc
表示数组argv[]
的长度。argv
是一个由参数组成的数组。- 函数签名中的
optstring
表示该程序可处理的选项,例如r:ls:w
,表示该程序可以处理rlsw
这四个选项,其中r
和s
后面跟着冒号:
表示这两个选项需要参数。如果用户传入了这四个选项之外的字符,程序将识别不了。 optarg
用于存放当前选项的参数。optind
表示当前处理的参数在argv
中的序号。opterr
如果是非零值,则当getopt()
遇到错误时向控制台输出错误信息。optopt
用于存放无法识别的选项。
每次调用getopt()
都会得到一个字符(虽然返回类型是int
),这个字符可能是一个参数,或者特殊符号。返回什么字符遵循下面的规则:
- 如果选项有一个关联值,则外部变量
optarg
指向这个值。 - 如果选项处理完毕,
getopt()
返回-1
,特殊参数--
将使getopt()
停止扫描选项。 - 如果遇到一个无法识别的选项,
getopt()
返回一个问号?
,并把它保存到外部变量optopt
中。 - 如果一个选项要求有一个参数,但用户没有提供这个值,
getopt()
通常将返回一个问号?
,并且向控制台打印错误信息。但是可以在选项字符串optstring
的开头加上:
,以让getopt()
,不打印错误信息,并返回一个冒号:
。
下面是一个例子:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[]){
int opt;
while((opt = getopt(argc, argv, ":lsf:r")) != -1){
switch (opt){
case 'l':
case 's':
case 'r':
printf("option: %c\n", opt);
break;
case 'f':
printf("file name: %s\n", optarg);
break;
case ':':
printf("option needs a value\n");
break;
case '?':
printf("unknown option: %c\n", optopt);
break;
}
}
for(; optind < argc; optind++){
printf("argument: %s\n", argv[optind]);
}
return 0;
}
该程序接受lsfr
四个字符作为选项,并且选项f
要求用户输入参数。
如果想了解更多细节,可以在这个程序上做各种实验,例如将:lsf:r
中第一个:
去掉并故意输入无法识别的选项。或者故意不遵循Linux
标准,在选项前面不加上-
等,总之就是要手动制造bug
通过犯错进行学习。
getopt_long()
上面介绍的getopt()
函数只是针对以-
开头的单字符的选项。除了对单字符的选项进行处理,Linux
还提供了处理长字符串(以--
开头)的函数,这就是getopt_long()
。下面是一个例子:
#include <unistd.h>
#include <stdio.h>
#include <getopt.h>
#define _GNU_SOURCE //必须定义这个宏才能使用getopt_long
int main(int argc, char * argv[]){
int opt;
struct option longopts[]={
{"initialize", 0, NULL, 'i'},
{"file", 1, NULL, 'f'},
{"list", 0, NULL, 'l'},
{"restart", 0, NULL, 'r'},
{0,0,0,0}
};
while ((opt = getopt_long(argc, argv, ":if:lr", longopts, NULL)) != -1){
switch (opt) {
case 'i' :
case 'l' :
case 'r' :
printf("option: %c\n", opt);
break;
case 'f' :
printf("file name: %s\n", optarg);
break;
case ':' :
printf("option needs a value\n");
break;
case '?' :
printf("unknown option: %c\n", optopt);
break;
}
}
for(;optind < argc; optind++){
printf("argument: %s\n", argv[optind]);
}
return 0;
}
getopt_long()
的函数声明和要用到的全局变量如下:
int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);
extern char *optarg;
extern int optind, opterr, optopt;
其中全局变量的用法跟getopt()
一致,但是getopt_long()
相较于getopt()
多了两个参数,他们分别是option数组
和一个变量指针。option数组
表示每个选项的行为,而变量指针相当于长选项版的optind
,对于每个识别的长选项,它在长选项数组中的索引就写入该变量。
跟getopt()
返回检测到的单字符参数不一样,getopt_long()
必须手动指定当检测到长参数时,应该返回哪个单字符。指定的方法就是在结构体struct option
中说明。
结构体option
的声明为:
struct option {
const char *name;
int has_arg;
int *flag;
int val;
}
他们的含义为:
name
:长选项的名字。缩写也可以接受,只是不要与其他选项搞混。has_arg
:该选项是否带参数。0
表示不带参数,1
表示必须带参数,2
表示可选参数。flag
:设置为NULL
表示当找到选项时,getopt_long()
返回成员val
里给出的值。否则,getopt_long()
返回0
并将val
的值写入flag
指向的变量。val
:getopt_long()
为该选项返回的值。