一、程序参数

  • 当一个用C语言编写的Linux或UNIX程序运行时,它是从main函数开始的。对这些程序而言,main函数的声明如下所示:

  • argc:​程序参数个数
  • argv:​一个代表参数自身的字符串数组,argv[0]必为程序名,argv[1]开始才是程序的参数


int main(int argc,char *argv[])
  • 你可能也会看到Linux的C程序将main函数简单的声明为:

  • 这样的main函数返回值类型默认为int
  • argc和argv仍在,但是由于没有声明它们,就不能使用它们

main()


例如

​./myprog left right 'and center'​


  • argc:4
  • argv:{"myprog","left","right","center"}


二、程序参数的使用规范

  • 命令行选项很常用,因此按相同的方式使用它们对程序的使用者来说是很有好处的。过去,每个工具程序采用它们各自的方式来使用命令行选项,这带来了一些混乱。例如,请看下面这些命令使用 参数的方式:

Linux(程序设计):19---main函数参数处理(getopt、getopt_long)_#include


  • ①我们建议在应用程序中,所有的命令行开关都应以一个短横线开头,其后包含单个字母或数字。 如果需要,不带后续参数的选项可以在一个短横线后归并到一起。所以,上面的两个ls命令示例就遵循了以上准则
  • ②如果某个选项需要值,则该值应作为独立的参数紧跟在该选项后。dd命令示例违背了 这一准则,因为它使用了多字符的选项,而且选项未以短横线开头(if=/dev/fd0),而tar命令则把 选项和它们的值完全分开!
  • ③我们建议最好能为单字符开关增加一个更长的、更有意义的开关名,这样 你就可以使用-h或--help选项来获得帮助了

演示案例

//args.c

#include <stdio.h>

int main(int argc,char *argv[])
{
int count;
for(count=0;count<argc;count++){
if(argv[count][0]=='-'){
printf("option:%s\n",argv[count]+1);
}else{
printf("argument:%s\n",argv[count]);
}
}
return 0;
}

Linux(程序设计):19---main函数参数处理(getopt、getopt_long)_#include_02


三、命令行开关函数(getopt)

#include <unistd.h>
int getopt(int argc, char * const argv[],const char *optstring);

extern char *optarg;
extern int optind, opterr, optopt;

参数

  • argc,argv:​分别为main函数的argc和argv
  • optstring:​字符串列表,该字符串告诉getopt哪些选项可用,以及它们是否有关联值
  • 列表中的一个字符代表一个单字符选项,如果一个字符后面紧跟着一个冒号(:),代表这个选项有一个关联值作为下一个参数

返回值

  • getopt的返回值是argv数组中的下一个选项字符(如果有的话)。循环调用getopt就可以依次得 到每个选项

getopt函数的行为

  • ①如果选项有一个关联值,则外部​变量optarg​指向这个值
  • ②如果选项处理完毕,getopt返回-1
  • ③特殊参数“--”将使getopt停止扫描选项
  • ④如果遇到一个无法识别的选项,getopt返回一个问号(?),并把它保存到​外部变量optopt中
  • ⑤如果一个选项要求有一个关联值(例如例子中的-f),但用户并未提供这个值,getopt通常将 返回一个问号(?)。如果我们将选项字符串(optstring)的第一个字符设置为冒号(:),那么getopt将在用户未提供值的情况下返回冒号(:)而不是问号(?)

optind变量

  • 外部变量optind被设置为下一个待处理参数的索引。getopt利用它来记录自己的进度。程序很少需要对这个变量进行设置。当所有选项参数都处理完毕后,optind将指向argv数组尾部可以找到其余参数的位置
  • 有些版本的getopt会在第一个非选项参数处停下来,返回-1并设置optind的值。但是Linux版本不同,如果参数中出现了非选项参数,那么getopt​重写了argv数组,把所有非选项参数集中在一起,非选项参数从argv[optind]位置开始​。对GNU版本 的getopt而言,这一行为是由环境变量POSIXLY_CORRECT控制的,如果它被设置,getopt就会在第 一个非选项参数处停下来

opterr变量

  • 还有些getopt版本会在遇到未知选项时打印出错信息
  • 注意,根据 POSIX规范的规定,如果opterr变量是非零值,getopt就会向stderr打印一条出错信息

案例

getopt(argc,argv,"if:lr");

  • 这个调用告诉我们,getopt会对-i、-f、-l、-r选项进行操作,由于f后面跟着一个冒号(:),因此-f选项的后面要跟着一个值

编程案例

//argopt.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc,char *argv[])
{
int opt;

//对选项参数进行处理,处理完毕之后getopt返回-1
//opt为每次返回的选项
while((opt=getopt(argc,argv,":if:lr"))!=-1)
{
switch(opt)
{
case 'i':
case 'l':
case 'r':
printf("option:%c\n",opt);
break;
case 'f':
printf("filename:%s\n",optarg);
break;
case ':':
printf("option needs a value\n");
break;
case '?':
printf("unknow option:%c\n",optopt);
break;
}
}

//所有选项都处理完毕后,程序像以前一样把其余参数都打印出来,但这里从optind位置开始
for(;optind<argc;optind++){
printf("argument:%s\n",argv[optind]);
}

exit(EXIT_SUCCESS);
}

  • 运行程序①:

  • -i、-l、-r选项依次被识别
  • 遍历到'hi there'的时候,由于getopt的字符串列表中没有,所以就跳过了接着遍历下面的-f选项
  • -f选项后面跟着fread.c,系统会将fread.c这个字符串保存在optarg中,我们通过打印optarg来打印fread.c
  • 由于-q选项没有在getopt函数的字符串列表中,所以显示“unknow option”

Linux(程序设计):19---main函数参数处理(getopt、getopt_long)_main函数_03

  • 运行程序②:
  • -f没有执行值,因为getopt的第三个参数字符串是以“:”开头的,因此getopt返回的是“:”,因此匹配第5个case,打印“option needs a value”

Linux(程序设计):19---main函数参数处理(getopt、getopt_long)_字符串_04

  • 运行程序③:
  • 由于我们将-i参数写到了-f的后面,程序误认为-i是-f选项的值,因此打印了下面的结果

Linux(程序设计):19---main函数参数处理(getopt、getopt_long)_#include_05


四、getopt_long

#include <getopt.h>
int getopt_long(int argc, char * const argv[],const char *optstring,
const struct option *longopts, int *longindex);
  • GNU C函 数库包含getopt的另一个版本,称作getopt_long,它接受以双划线(--)开始的长参数

参数

  • argc,argv:​main函数的参数
  • optstring:​字符串列表
  • longopts:​一个struct option的结构体,描述每个长选项并告诉getopt_long如何处理它们。长选项数组由一些类型为struct option的结构组成,每个结构描述了一个长选项的行为。该数 组必须以一个包含全0的结构结尾。
  • longindex:​一个变量指针,它可以作为optind的长选项版本使用。对于每个识别的长选项,它在长选项数组中的索引就写入该变量

struct option结构体

  • 要使用该结构体,必须定义_GNU_SOURCE宏定义

#include <getopt.h> #define _GNU_SOURCE 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为该选项返回的值

演示案例

//longopt.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>

#define _GNU_SOURCE
int main(int argc,char *argv[])
{
int opt;
struct option longopts[]={
{"initialize",0,NULL,'i'},
{"file",1,NULL,'f'},
{"list",0,NULL,'1'},
{"restart",0,NULL,'r'},
{0,0,0,0},
};

//本例中,我们不需要longindex参数,将其置位NULL
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("filename:%s\n",optarg);
break;
case ':':
printf("option needs a value\n");
break;
case '?':
printf("unknow option:%c\n",optopt);
break;
}
}

for(;optind<argc;optind++){
printf("argument:%s\n",argv[optind]);
}

exit(EXIT_SUCCESS);
}

Linux(程序设计):19---main函数参数处理(getopt、getopt_long)_#include_06Linux(程序设计):19---main函数参数处理(getopt、getopt_long)_main函数_07