Fork me on GitHub

程序参数简述

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

int main(int argc, char *argv[])

其中argc是程序参数的个数,argv是一个代表参数自身的字符串数组
也可以如下声明

main()

这样也行,因为默认的返回值类型是int,而且函数中不用的形式参数不需要声明。argc和argv仍在,但如果不声明它们,你就不能使用它们
无论操作系统何时启动一个新程序,参数argc和argv都被设置并传递给main。这些参数通常由另一个程序提供,这个程序一般是shell,它要求操作系统启动该新程序。shell接受用户输入的命令行,将命令行分解成单词,然后把这些单词放入argv数组。请记住:Linux的shell一般会在设置argc和argv之前对文件名参数进行通配符扩展,而MS-DOS的shell则期望程序接受带通配符的参数,并执行它们自己的通配符扩展。

通常,你可以使用一个以短横线(-)开头的命令行参数来设置这些所谓的标志(flag)或开关(switch)。

sort -r /etc/hosts

程序参数示例

下面这个程序args.c对其参数进行检查:

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

int main(int argc,char *argv[])
{
	int arg; //定义数组游标,用于遍历数组
	for(arg=0;arg<argc;arg++)
	{
		if(argv[arg][0] == '-') // 判断参数的第一个字符是否为'-'
		{
			printf("option:%s\n",argv[arg]+1); // 输出选项指令
		}else
		{
			printf("argument %d:%s\n",arg,argv[arg]); //  输出参数
		}
	}
	exit(0);
}

结果如下:

实验解析:

这个程序利用计数参数argc建立一个循环来检查所有的程序参数。它通过检查首字母是否是短横线来发现选项。
在本例中,如果打算支持-l选项和-r选项,那么我们就忽略了一个事实:-lr选项应该和-l –r一样处理。
X/Open规范(可以在http://opengroup.org/上找到)定义了命令行选项的标准用法(工具语法指南),同时定义了在C语言程序中提供命令行开关的标准编程接口:getopt函数。

getopt

为了帮助我们遵循这些准则,Linux提供了getopt函数,它支持需要关联值和不需要关联值的选项,而且简单易用。

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

getopt函数将传递给程序的main函数的argcargv作为参数,同时接受一个选项指定字符串optstring,该字符串告诉getopt哪些选项可用,以及它们是否有关联值。optstring只是一个字符列表,每个字符代表一个单字符选项。如果一个字符后面紧跟一个冒号(😃,则表明该选项有一个关联值作为下一个参数。bash中的getopts命令执行类似的功能
调用方式

getops(argc,argv,'if:lr');
它允许几个简单的选项:-i、-l、-r和-f,其中-f后面紧跟一个文件名参数。使用相同的参数,但以不同的顺序来调用命令将改变程序的行为。

getopt的返回值是argv数组中的下一个选项符。循环调用getopt就可以依次得到每个选项。

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

外部变量optind被设置为下一个待处理参数的索引,getopt利用它来记录自己的进度。程序很少需要对这个变量进行设置,当所有选项参数都处理完毕后,optind将指向argv数组尾部可以找到其余参数的设置

有些版本的getopt会在第一个非选项参数处停下来,返回-1并设置optind的值。而其他一些版本,如果Linux提供的版本,能够处理出现在程序参数中任意位置的选项。
注意,在这种情况下,getopt实际上重写了argv数组,把所有非选项参数都集中在一起,从argv[optind]位置开始。对GNU版本的getopt而言,这一行为是由环境变量POSIXLY_CORRECT控制的,如果它被设置,getopt就会在第一个非选项参数处停下来。此外,还有些getopt版本会在遇到未知选项时打印出错误信息。注意,根据POSIX规范,如果opterr变量是非零值,getopt就会向stderr打印一条出错误信息

实验 getopt函数

在这个实验中,你将在程序中使用getopt函数,并将新程序命名为argopt.c:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
	int 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("unknown option: %c\n",optopt);
				break;
		}
}
	for(;optind < argc ; optind++)
		printf("argument: %s\n",argv[optind]);
	exit(0);
}

实验解析

这个程序循环调用getopt对选项参数进行处理,直到处理完毕,此时getopt返回-1.每个选项(包括未知选项和缺少关联值的选项)都有相应的处理动作。根据使用的getopt版本,你看到的输出可能和上面略有不同,尤其是出错信息部分,但含义都是明确的
当所有选项都处理完毕后,程序像以前一样把其余参数都打印出来,但这次是从optind位置开始

getopt_long

GNU C函数库包含getopt的另一个版本,称作getopt_long,它接受以双划线(--)开始的长参数

实验getopt_long

你可以使用getopt_long创建一个新版本的示例程序,它可以使用与前面选项等效的长参数选项:

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

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("filename: %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]);
	exit(0);
}


事实上,新的长选项和原来的单字符选项可以混合使用。只要它们能够被区分开,长选项也可以缩写。有关联值的长选项可以按照格式--option=value作为单个参数给出,如下所示

实验解析

getopt_long函数比getopt多两个参数。第一个附加参数是一个结构数组,它描述了每个长选项并告诉getopt_long如何处理它们。第二个附加参数是一个变量指针,它可以作为optind的长选项版本使用。对于每个识别的长选项它在长选项数组中的索引就写入该变量。在本例中,你不需要这一信息,因此第二个附加参数是NULL

长选项数组由一些类型为struct option的结构组成,每个结构描述了一长选项的行为。该数组必须以一个包含全0的结构结尾。

长选项结构在头文件getopt.h中定义,并且该关文件必须与常量_GNU_SOURCE一同包含进来,该常量启用getopt_long功能

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为该选项返回的值

要了解GNU对getopt扩展的其他选项及相关函数,主参考getopt的手册页

posted on 2019-11-28 17:18  anyux  阅读(488)  评论(0)    收藏  举报