`
helpbs
  • 浏览: 1165204 次
文章分类
社区版块
存档分类
最新评论

Linux环境(一)--程序参数

 
阅读更多
当我们为Linux编写程序时,我们必须考虑到程序会运行在多任务环境下。这就意味着多个程序会同时运行,并且共享机器资源,例如内存,磁盘空间以及CPU周期。也许在同一时刻会一个程序多个实例在运行。这时最为重要的就是这些程序之间不会相互影响,彼此清楚其周边环境,同时也要正确的运行以避免冲突,例如与另一个程序同时试着写入相同的文件等。

在这一章,我们将会讨论程序执行的环境,他们如何使用环境来得到有关操作环境的信息,以及这些程序用户如何改变其行为。具体的说,我们会讨论下面内容:

向程序传递参数
环境变量
查看时间信息
临时文件
得到用户以及主机的信息
记录以及配置日志消息
发现系统的限制

程序参数

当一个使用C语言编写的Linux或是Unix程序运行时,他由main函数开始执行。对于这些程序来说,main函数声明为

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

在这里argc是程序参数的个数,而argv是一个代表参数本身的字符串数组。

有时我们也会看到Linux的C程序简单的声明为

main()

这仍然可以正常工作,因为返回类型默认为int型,而在函数不需要的常规参数并不需要声明。argc与argv仍然存在,但是如果我们不进行声明,我们就没有办法使用。

当操作系统启动一个新程序时,argc与argv就会被设置,并传递给main。这些参数通常是由另一个程序来提供的,通常是请求操作系统执行一个新程序的shell。shell会读取所给定的命令行参数,将其分为单个的单词,并且用他们来设置argv数组。记住,在argc与argv被设置之前,Linux shell通常会对文件名参数执行通配符扩展,而MS-DOS shell会期望程序接受带有通配符的参数,并且执行他们自己的通配符扩展。

例如,如果我们为shell提供下面的命令:

$ myprog left right ‘and center’

myprog程序就会用下列参数来启动main函数:

argc: 4
argv: {“myprog”, “left”, “right”, “and center”}

注意,参数个数包括程序本身的名字,并且argv数组包含程序名字作为其第一个元素,argv[0]。因为我们在shell命令中使用了引号,第四个参数包含一个带有空格的字符串。

如果我们使用过ISO/ANSI C编写过程序,我们就会对这些内容感到熟悉。main参数相对应于shell脚本中的位置参数,$0,$1,依次类推。然而ISO/ANSI C说明main必须返回int,X/Open的描述包含上面给出的显示声明。

命令行参数对于向程序传递信息是十分有用的。例如,我们可以在一个数据库程序中使用命令行参数来传递我们希望使用的数据库名,这样就可以允许我们在多个数据库上使用相同的程序。许多实用程序也使用命令行参数来改变他们的行为或是设置选项。我们可以使用带有短划线的命令行参数来设置这些所谓的标记,或是开关。例如,sort程序带有一个开关来反转通常的排列顺序。

$ sort -r file

命令行参数十分常见,而且恰当的使用对于使用我们程序的人来说确实是一个极大的帮助。过去,所有的实用程序都实现了他们各自的命令选项方法,这就造成混乱。例如,看一下下面这些命令读取参数的方式:

$ tar cvfB /tmp/file.tar 1024
$ dd if=/dev/fd0 of=/tmp/file.dd bs=18k
$ ls -lstr
$ ls -l -s -t -r

所有的命令行参数都应以一个短划线开始,并且由一个字母或是数字组成。没有更多参数的选项可以在短划线后组织在一起。所以上面显示的两个ls命令都遵守了这个约定。如果选项需要一个单独的参数,那么可以在其后跟一个值。dd命令的例子并没有遵守这个规则,他使用了多个字符选项,并且没有以短划线开始(if=/dev/fdo);而tar命令将其选项与值进行完全的分离。

另外一些程序的缺点就是使用选项+x来执行与-x相反的功能。

正如我们所看到的,如果不使用特殊的格式处理,记住所有这些程序选项的顺序和意义是相当困难的。通常,唯一的资源就是使用一个-h(help)选项,或者是man页。正如我们在后面将会看到,getopt为这些问题提供了一个灵巧的解决办法。但是现在,让我们看一下所传递的程序参数的处理。

试验--程序参数

这里是一个程序,args.c,来检测他自己的参数:

#include <stdio.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);
}

当我们运行这个程序时,他只是打印出其参数与检测选项。其目的在程序读取一个字符串参数以及一个由-f选项引入的可选的文件名参数。同时也定义了其他的选项。

$ ./args -i -lr ‘hi there’ -f fred.c
argument 0: args
option: i
option: lr
argument 3: hi there
option: f
argument 5: fred.c

工作原理

程序只是简单的使用程序计数argc设置一个循环来检测所有的程序参数。他通过查找一个初始短划线来检测选项。

在这个例子中,如果我们的设计只是-l与-r是可用的,那么也许我们就会失去将-lr本应看作与-l -r相同的事实。

X/Open描述为命令行选项定义了一个标准用法,同时在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函数的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 <stdio.h>
#include <unistd.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);
}

现在,当我们运行这个程序时,我们发现所有的命令行参数被自动处理了:

$ ./argopt -i -lr ‘hi there’ -f fred.c -q
option: i
option: l
option: r
filename: fred.c
argopt: invalid option-—q
unknown option: q
argument: hi there

工作原理

程序会重复调用getopt来处理选项参数,直到没有选项需要处理为止,此时getopt会返回-1。这个动作会在每一个选项上执行,包括处理不可知的选项以有没有选项参数值的情况。依据我们的getopt版本,我们得到的输出与上面的与许不同,尤其是错误信息,但是意义是清楚的。

一旦所有的选项都被处理了,程序就会简单的打印出剩余的参数,这与前面的例子相同,但是却由optind开始的。

getopt_long

许多Linux程序同时会接受比我们在上面的例子中所使用的单字符选项更有意义的参数。GNU C库包含一个被称之为getopt_long的getopt版本,他可以接受由双短划线引入的所谓长参数。

我们使用getopt_long来创建我们上面例子程序的一个新版本,这样他就可以使用与我们的选项等同的长参数来调用了。

事实上,新的长选项与原始的单字符选项可以混合。只要他们存在不同,长选项也可以简写。带有参数的长选项可以--option=value的格式给出,如下所示:

$ ./longopt —init –l —file=fred.c ‘hi there’
option: i
option: l
filename: fred.c
argument: hi there

新程序longopt.c如下所示:

#include <stdio.h>
#include <unistd.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);
}

工作原理

与getopt相比,getopt_long带有两个额外的参数。第一个为一个结构数组,描述了长选项并且通知getopt_long如何来处理。第二个额外参数是一个指向可以用作长选项版本的optind变量的指针;对于每一个识别的长选项,他在长选项数组中的索引可以写入这个变量。在我们的例子中,我们并不需要这个信息,所以我们使用NULL作为第二个额外参数。

长选项数组是由一些type struct option的结构组成的,每一个描述了长选项的行为。这个数组必须以一个包含零的结构结束。

长选项结构定义在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为这个选项返回的值。

要了解其他与getopt的GNU扩展相关联的选项以及相关的功能,可以查看getopt的手册页。
分享到:
评论

相关推荐

    Linux-Socket-服务器编程实例.pptx

    Linux Socket服务器端编程实例 例:建立一个Linux TCP服务器,等待客户端的连接请求,一旦接收到客户端请求,将客户端的IP地址和端口号打印出来,并且向客户端发送"Hello!Socket communication world!"字符串,然后...

    Linux环境下PowerPC-NC运行性能参数的获取.pdf

    Linux环境下PowerPC-NC运行性能参数的获取.pdf

    静态编译的linux下ffmpeg-3.3.3版本及SDK库

    附件为Centos6.6环境下静态编译的ffmpeg-3.3.3版本linux可执行程序和SDK库,其中包含基本的x264、aac编解码库;还增加字幕渲染(drawtext参数)、水印叠加等功能;configure配置参数为: ./configure --target-os=...

    安装 SUSE Linux Enterprise Server --服务器版

    用程序各个字段以及 SUSE Linux Enterprise Server 支持的每个平台的安装类型 的概述,以及对安装过程的简短的说明。 SUSE Linux Enterprise Server 10 www.novell.com 2006 6 02 入门指南 入门指南 作者列表: Jörg...

    Linux移植及驱动应用程序课程设计任务书.docx

    具体任务有搭建嵌入式Linux开发环境、编译Bootloader、裁剪和编译Linux内核、制作和挂载NFS文件系统。 (2) 开发Linux按键驱动程序,驱动开发板上三个按键。 (3) 开发LED驱动程序或者PWM驱动程序。要求LED驱动程序能...

    grpc-1.30.2 Linux 编译静动态库

    默认编译是静态库,但考虑到 linux 上动态库使用较多,所以使用 -DBUILD_SHARED_LIBS=ON 参数编译为动态库。 在 centos 7 下使用 gcc 4.8.5 + cmake 3.16.9 编译,包含 bin, include, lib, lib64, share 五个目录,...

    嵌入式Linux程序设计案例与实验教程-实例代码

    实验2.1 嵌入式Linux开发环境的建立14 2.2 Linux C程序设计17 2.2.1 C程序设计概述17 2.2.2 Makefile介绍17 2.2.3 Makefile中的变量18 2.2.4 Makefile隐含规则19 实验2.2 Makefile与helloworld19 2.3 ...

    Linux C程序设计大全

    1.3 Linux环境下的其他编程语言 1.3.1 C++ 1.3.2 Java 1.3.3 Perl 1.3.4 Python 1.3.5 Ruby 1.3.6 PHP 第2章 控制结构 2.1 goto语句 2.1.1 C语言中的无条件跳转 2.1.2 使用goto语句进行出错处理 2.1.3 出错处理的...

    嵌入式Linux程序设计案例与实验教程(配套光盘)第一部分

    实验2.1 嵌入式Linux开发环境的建立14 2.2 Linux C程序设计17 2.2.1 C程序设计概述17 2.2.2 Makefile介绍17 2.2.3 Makefile中的变量18 2.2.4 Makefile隐含规则19 实验2.2 Makefile与helloworld19 2.3 Linux多...

    Linux程序设计 第4版.haozip01

    第4章 linux环境 114 4.1 程序参数 114 4.1.1 getopt 116 4.1.2 getopt_long 118 4.2 环境变量 120 4.2.1 环境变量的用途 122 4.2.2 environ变量 122 4.3 时间和日期 123 4.4 临时文件 129 4.5 用户信息 ...

    Linux简明教程.rar

    一、Linux的起源------------------------------------------------------------------------------------ 二、Linux的优势------------------------------------------------------------------------------------...

    linux下安装ImageMagick-6.5+JMagick -6.4 经测试成功的

    ImageMagick 是一个功能很强的图片处理程序。可应用在多种操作系统平台上。 安装ImageMagick和JMagick 如果安装了老版本ImageMagick的rpm包,请先删除 安装环境要求: 确认已经安装了zlib、freetype、libpng和jpeg-...

    Linux程序设计 第4版.haozip02

    第4章 linux环境 114 4.1 程序参数 114 4.1.1 getopt 116 4.1.2 getopt_long 118 4.2 环境变量 120 4.2.1 环境变量的用途 122 4.2.2 environ变量 122 4.3 时间和日期 123 4.4 临时文件 129 4.5 用户信息 ...

    lab2-c语言实现shell程序.zip-linux实验

    实验二:Linux 命令环境下 C/C++语言实践 2. 实验内容 基本任务 1:用 C/C++编写一个简单的 shell 程序,实现以下基本的命令。 1) 浏览目录和文件的各种属性 ls(可以不支持参数) 2) 回显命令 echo 3) 显示文件内容...

    vi/vim常用命令详解,让您轻松在linux环境下编辑/开发程序

    详细解释常用命令,简单易懂,让你轻松地掌握vim在linux环境下开发

    Linux环境数据库管理员指南

    2.4.10 Linux装载程序与引导盘 35 2.4.11 完成安装 36 2.4.12 配置服务器运行数据库 36 2.4.13 需要注意的事项 36 2.5 Linux的其他风格 36 2.6 小结 37 2.7 常见问答 38 第3章 在 Linux上安装并运行 Oracle 40 3.1 ...

    Linux-Auto-Customizer:用于自定义Ubuntu-Linux环境并自动安装与编程相关的软件的脚本

    install.sh脚本可以将某些自定义功能(取决于所接收的参数)应用于当前用户控制台和Ubuntu-Linux环境,例如本地函数,文件模板和全局变量。 同样,也可以安装第三方软件,包括其依赖项。 该uninstall.sh可用于卸载...

    嵌入式Linux应用程序开发详解

    在开发第一步,使用IDE可以方便地将程序代码编写到指定设备中,它包括调试功能,可以帮助检测代码错误,并可用于调整程序参数。 此外,开发者还可以使用串口通信工具,来发送和接收信号,调试和更新代码。生成图形...

    cmd操作命令和linux命令大全收集

    at ip time 程序名(或一个命令) /r 在某时间运行对方某程序并重新启动计算机 finger username @host 查看最近有哪些用户登陆 telnet ip 端口 远和登陆服务器,默认端口为23 open ip 连接到IP(属telnet登陆后的...

Global site tag (gtag.js) - Google Analytics