原博地址:
当我们为Linux编撰程序时,我们必须考虑到程序会运行在多任务环境下。这就意味着多个程序会同时运行,但是共享机器资源,比如显存,c盘空间以及CPU周期。似乎在同一时刻会一个程序多个实例在运行。这时最为重要的就是这种程序之间不会互相影响,彼此清楚其周边环境,同时也要正确的运行以防止冲突,比如与另一个程序同时试着写入相同的文件等。
在这一章,我们将会讨论程序执行的环境,她们怎么使用环境来得到有关操作环境的信息,以及这种程序用户怎样改变其行为。具体的说,我们会讨论下边内容:
向程序传递参数
环境变量
查看时间信息
临时文件
得到用户以及主机的信息
记录以及配置日志消息
发觉系统的限制
程序参数
当一个使用C语言编撰的Linux或是Unix程序运行时,他由main函数开始执行。对于这种程序来说,main函数申明为
intmain(intargc,char*argv[])
在这儿argc是程序参数的个数,而argv是一个代表参数本身的字符串字段。
有时我们也会见到Linux的C程序简单的申明为
main()
这依然可以正常工作,由于返回类型默认为int型,而在函数不须要的常规参数并不须要申明。argc与argv依然存在,而且假如我们不进行申明,我们就没有办法使用。
当操作系统启动一个新程序时,argc与argv都会被设置,并传递给main。那些参数一般是由另一个程序来提供的,一般是恳求操作系统执行一个新程序的shell。shell会读取所给定的命令行参数,将其分为单个的词组,而且用她们来设置argv字段。记住,在argc与argv被设置之前,Linuxshell一般会对文件名参数执行键值扩充,而MS-DOSshell会期望程序接受带有键值的参数redhat linux 9.0下载,而且执行她们自己的键值扩充。
比如,假如我们为shell提供下边的命令:
$myprogleftright‘andcenter’
myprog程序都会用下述参数来启动main函数:
argc:4
argv:{“myprog”,“left”,“right”,“andcenter”}
注意,参数个数包括程序本身的名子linux 命令行参数,但是argv链表包含程序名子作为其第一个元素,argv[0]。由于我们在shell命令中使用了冒号,第四个参数包含一个带有空格的字符串。
假如我们使用过ISO/ANSIC编撰过程序,我们都会对那些内容倍感熟悉。main参数相对应于shell脚本中的位置参数,$0,$1,依次类推。但是ISO/ANSIC说明main必须返回int,X/Open的描述包含前面给出的显示申明。
命令行参数对于向程序传递信息是非常有用的。比如,我们可以在一个数据库程序中使用命令行参数来传递我们希望使用的数据库名,这样就可以容许我们在多个数据库上使用相同的程序。许多实用程序也使用命令行参数来改变她们的行为或是设置选项。我们可以使用带有短划线的命令行参数来设置那些所谓的标记,或是开关。诸如,sort程序带有一个开关来反转一般的排列次序。
$sort-rfile
命令行参数非常常见,但是恰当的使用对于使用我们程序的人来说确实是一个极大的帮助。过去,所有的实用程序都实现了她们各自的命令选项方式,这就导致混乱。诸如,看一下下边这种命令读取参数的方法:
$tarcvfB/tmp/file.tar1024
$ddif=/dev/fd0of=/tmp/file.ddbs=18k
$ls-lstr
$ls-l-s-t-r
所有的命令行参数都应以一个短划线开始,但是由一个字母或是数字组成。没有更多参数的选项可以在短划线后组织在一起。所以前面显示的两个ls命令都遵循了这个约定。倘若选项须要一个单独的参数linux 命令行参数,这么可以在其后跟一个值。dd命令的事例并没有遵循这个规则,他使用了多个字符选项,而且没有以短划线开始(if=/dev/fdo);而tar命令将其选项与值进行完全的分离。
另外一些程序的缺点就是使用选项+x来执行与-x相反的功能。
正如我们所见到的,假如不使用特殊的格式处理,记住所有那些程序选项的次序和意义是相当困难的。一般,惟一的资源就是使用一个-h(help)选项,或则是man页。正如我们在前面将会看见,getopt为这种问题提供了一个灵巧的解决办法。并且如今,让我们看一下所传递的程序参数的处理。
试验--程序参数
这儿是一个程序,args.c,来检查他自己的参数:
#include
intmain(intargc,char*argv[])
intarg;
for(arg=0;arg<a style='color:#0000CC;font-size:16px;' rgc;arg++){
if(argv[arg][0]==‘-’)
printf(“option:%s/n”,argv[arg]+1);
else
printf(“argument%d:%s/n”,arg,argv[arg]);
exit(0);
当我们运行这个程序时,他只是复印出其参数与测量选项。其目的在程序读取一个字符串参数以及一个由-f选项引入的可选的文件名参数。同时也定义了其他的选项。
$./args-i-lr‘hithere’-ffred.c
argument0:args
option:i
option:lr
argument3:hithere
option:f
argument5:fred.c
工作原理
程序只是简单的使用程序计数argc设置一个循环来检查所有的程序参数。他通过查找一个初始短划线来测量选项。
在这个事例中,假如我们的设计只是-l与-r是可用的,这么显然我们都会丧失将-lr本应看作与-l-r相同的事实。
X/Open描述为命令行选项定义了一个标准用法,同时在C程序中为提供命令行开关提供定义了一个标准程序插口:getopt函数。
getopt
为了帮助我们遵循这种规则,Linux为我们提供了getopt函数,他支持带值与不带值的选项用法,而且使用简单。
#include
intgetopt(intargc,char*constargv[],constchar*optstring);
externchar*optarg;
externintoptind,opterr,optopt;
getopt函数读取传递给程序的main函数的argc与argv参数以及一个选项描述符字符串,这个字符串通知getopt为这个程序定义了什么选项,以及那些选项是否有相关联的值。optstring只是一个简单的字符列表,每一个代表一个字符选项。假如一个字符后跟有一个逗号,这么他表明这个选项有一个与其相关联的值,应读取作为下一个参数。bash中的getopts命令有着相类似的功能。
比如,下边的调用可以拿来处理我们上面的事例:
getopt(argc,argv,“if:lr”);
他容许简单的选项-i,-l,-r,以及后跟文件名参数的-f选项。使用相同的参数并且以不同的次序调用这个命令会改变其行为。当我们遇见前面的示例代码时可以进行尝试。
getopt的返回结果是argv字段中下一个选项字符(假如有)。我们重复调用getopt来依次得到每位选项。他有下边的行为:
倘若选项带有一个参数值,这么这个值是由外部变量optarg来指向的。
当没有选项要进行处理时,getopt会返回-1。一个特殊的参数,--,会促使getopt停止搜索。
假如有一个不可辨识的选项,他会返回?,但是选项会储存在外部变量optopt中。
假如一个选项须要一个参数值(比如我们事例中的-f),然而却没有参数值时,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
#include
intmain(intargc,char*argv[])
intopt;
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(“optionneedsavalue/n”);
break;
case‘?’:
printf(“unknownoption:%c/n”,optopt);
break;
for(;optind<a style='color:#0000CC;font-size:16px;' rgc;optind++)
printf(“argument:%s/n”,argv[optind]);
exit(0);
如今,当我们运行这个程序时,我们发觉所有的命令行参数被手动处理了:
$./argopt-i-lr‘hithere’-ffred.c-q
option:i
option:l
option:r
filename:fred.c
argopt:invalidoption-—q
unknownoption:q
argument:hithere
工作原理
程序会重复调用getopt来处理选项参数,直至没有选项须要处理为止,此时getopt会返回-1。这个动作会在每一个选项上执行,包括处理不可知的选项以有没有选项参数值的情况。根据我们的getopt版本,我们得到的输出与前面的与许不同linux系统官网,尤其是错误信息,而且意义是清楚的。
一旦所有的选项都被处理了,程序都会简单的复印出剩余的参数,这与上面的事例相同,然而却由optind开始的。
getopt_long
许多Linux程序同时会接受比我们在前面的事例中所使用的单字符选项更有意义的参数。GNUC库包含一个被称之为getopt_long的getopt版本,他可以接受由双短划线引入的所谓长参数。
我们使用getopt_long来创建我们里面事例程序的一个新版本,这样他就可以使用与我们的选项等同的长参数来调用了。
事实上,新的长选项与原始的单字符选项可以混和。只要她们存在不同,长选项也可以缩写。带有参数的长选项可以--option=value的格式给出,如下所示:
$./longopt—init–l—file=fred.c‘hithere’
option:i
option:l
filename:fred.c
argument:hithere
新程序longopt.c如下所示:
#include
#include
#define_GNU_SOURCE
#include
intmain(intargc,char*argv[])
intopt;
structoptionlongopts[]={
{“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(“optionneedsavalue/n”);
break;
case‘?’:
printf(“unknownoption:%c/n”,optopt);
break;
for(;optind<a style='color:#0000CC;font-size:16px;' rgc;optind++)
printf(“argument:%s/n”,argv[optind]);
exit(0);
工作原理
与getopt相比,getopt_long带有两个额外的参数。第一个为一个结构链表,描述了长选项而且通知getopt_long怎样来处理。第二个额外参数是一个指向可以用作长选项版本的optind变量的表针;对于每一个辨识的长选项,他在长选项链表中的索引可以写入这个变量。在我们的事例中,我们并不须要这个信息,所以我们使用NULL作为第二个额外参数。
长选项字段是由一些typestructoption的结构组成的,每一个描述了长选项的行为。这个字段必须以一个包含零的结构结束。
长选项结构定义在getopt.h中,并且必须使用_GNU_SOURCE常包含,拿来容许getopt_long功能:
structoption{
constchar*name;
inthas_arg;
int*flag;
intval;
};
结构的成员如下:
name长选项的名子。只要她们的缩写不与其他的选项冲突就可以被接受。
has_arg这个选项是否带有参数。倘若不须要参数,将其设置为0;假如必须有一个参数值,将其设置为1;假如有一个可选参数,将其设置为2。
flag将其设置为NULL,致使getopt_long在查找到这个选项时返回val中定的值。否则,getopt_long会返回0,而且将val的值写入由flag所指向的变量。
valgetopt_long为这个选项返回的值。
要了解其他与getopt的GNU扩充相关联的选项以及相关的功能,可以查看getopt的指南页。