`
javasee
  • 浏览: 968671 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

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的手册页。
分享到:
评论

相关推荐

    wrapper-linux-x86-64-3.5.55 社区版

    本篇将深入探讨"wrapper-linux-x86-64-3.5.55 社区版"这个软件包,它主要用于在Linux环境下运行Java应用程序。Wrapper技术是将Java程序封装到一个可执行的系统服务或守护进程中,使其能够更方便地管理、监控和自动化...

    arm-linux-gcc-4.4.3.tar.gz

    标题中的"arm-linux-gcc-4.4.3.tar.gz"是一个针对ARM架构的Linux GCC交叉编译器的压缩包,版本为4.4.3。这个文件通常用于在非ARM设备(如Ubuntu系统)上构建能在ARM处理器上运行的Linux应用程序。下面将详细介绍...

    arm-linux-gcc-4.8.4.tar.gz

    在这种情况下,ARM-Linux-GCC-4.8.4是专为基于ARM架构的Linux系统设计的,可以在非ARM平台(如x86架构的PC)上编译出适用于ARM设备的Linux应用程序。 二、GCC与4.8.4版本 GCC,全称为GNU Compiler Collection,是...

    arm-linux-gcc-4.3.2

    在“arm-linux-gcc-4.3.2.tgz”文件中,我们可以通过解压获取到一系列用于构建和调试ARM Linux应用程序的工具和库。tgz是一种常见的文件打包格式,结合了tar(归档)和gzip(压缩)的功能,可以有效地减小文件体积,...

    arm-vfp-linux-gnu-4.3.2交叉编译工具链及安装

    `arm-vfp-linux-gnu-4.3.2`就是这样一个专为带有硬件浮点单元(VFP,Vector Floating Point)的ARM9处理器设计的交叉编译工具链,它允许开发者在具有更强计算能力的工作站上完成编译工作,然后将编译好的二进制文件...

    Linux实验十一 arm-Linux交叉编译环境搭建

    本次实验的主要目标是让学生熟练掌握在Linux环境下构建arm-Linux嵌入式开发环境的过程,包括交叉编译器的搭建、安装与配置方法。通过实践操作,学生能够更好地理解嵌入式系统的开发流程和技术要点。 #### 实验内容 ...

    wrapper-linux-x86-64-3.5.55 专业版

    在描述中,"wrapper-linux-x86-64-3.5.55 专业版"仅仅是一个版本标识,它表明该软件包是针对64位Linux环境的,且版本号为3.5.55。专业版通常比免费或社区版提供了更多的特性,如增强的安全性、性能优化、更全面的...

    eclipse-cpp-2023-06-R-linux-gtk-x86-64.tar.gz

    《Eclipse C++ IDE在Linux x86_64环境中的深度解析与应用》 Eclipse作为一款全球知名的开源...通过正确安装、配置和熟练使用,开发者可以在Linux环境中高效地进行C++项目开发,享受到Eclipse带来的便捷与强大功能。

    linux-wlan-ng-0.2.9.rar_linux wlan_linux-wlan-ng_linux-wlan-ng 0

    Linux-WLAN-ng是针对Linux操作系统的一个开源无线网络驱动程序,主要设计用于支持使用Prism芯片的无线网卡。这个驱动程序版本为0.2.9,包含在名为"linux-wlan-ng-0.2.9.rar"的压缩文件中。在Linux系统中,驱动程序...

    LINUX下的MODBUS-RTU驱动程序

    在Linux环境中,MODBUS-RTU驱动程序是一个关键组件,用于实现MODBUS通信协议,这是一种广泛应用的工业设备间通信标准。MODBUS允许不同设备通过串行连接交换数据,如PLC(可编程逻辑控制器)、传感器、执行器等。在...

    eclipse-java-indigo-SR2-linux-gtk-x86_64

    【标题】"eclipse-java-indigo-SR2-linux-gtk-x86_64" 是一个针对64位Linux系统,基于GTK图形界面的Eclipse IDE版本,属于Eclipse Indigo Service Release 2 (SR2)。这个版本是Java开发者的专用版本,包含了对Java...

    peak-linux-driver-8.12.0.tar.gz

    在Linux环境中,驱动程序是操作系统与硬件设备之间的桥梁,它们允许操作系统识别和控制硬件设备,如网络适配器、显卡、声卡等。"peak-linux-driver-8.12.0"可能是某个特定硬件厂商发布的驱动程序更新,版本号8.12.0...

    mysql for Linux (mysql-standard-4.1.22-pc-linux-gnu-i686.tar.gz )

    总结,`mysql-standard-4.1.22-pc-linux-gnu-i686.tar.gz` 是Linux环境下MySQL的一个旧版本,但其安装、配置和使用的基本原理依然适用于现代MySQL版本。了解这些基本概念和操作,对于任何Linux系统的数据库管理员来...

    rxtx-2.2-linux-windowx-x64.zip

    在Linux环境下,"mfz-rxtx-2.2-20081207-linux-x86_64.zip"提供了64位Linux版的RXTX库。在Linux系统中,我们需要将解压后的库文件(如librxtxSerial.so)放置在适当的库目录(如/lib64/)中,并确保使用ldconfig命令...

    arm-linux-gcc-3.3.2_安装交叉编译器

    通过以上步骤,我们可以成功地在Linux环境下安装并配置ARM-Linux-GCC-3.3.2交叉编译器。这一工具对于嵌入式系统开发至关重要,能够帮助开发者在宿主机上构建针对ARM架构目标系统的应用程序,极大地提高了开发效率。...

    ubuntu 20.04 下安装arm-linux-gnueabi和编译对应的GDB.docx

    `arm-linux-gnueabi`就是这样的一个工具链,它允许你在非ARM系统上构建适用于ARM处理器的Linux应用程序。在Ubuntu中,可以通过官方软件仓库来安装`arm-linux-gnueabi`的编译器部分,即`gcc-arm-linux-gnueabi`。 **...

    Linux -lglib-2.0 缺失时要用的 arm64下的链接库

    在Linux系统中,`-lglib-2.0` 是一个链接器选项,用于指示编译器在构建可执行程序时链接GLib库版本2.0。GLib是GObject系统的基础,它提供了核心数据类型、低级内存管理、线程支持、事件循环和定时器等关键功能,广泛...

    linux-adc-key驱动.rar

    RK3308是一款基于ARM Cortex-A53架构的系统级芯片,常用于物联网(IoT)设备和智能硬件中,其ADC功能对于采集环境或传感器数据至关重要。 在Linux系统中,ADC驱动通常作为字符设备或块设备驱动存在,负责将硬件的ADC...

    openocd-esp32-linux64-0.10.0-esp32-20191114.tar.gz

    对于初学者,理解OpenOCD的配置文件和命令行参数至关重要,这将直接影响到调试的效率和效果。 总的来说,OpenOCD ESP32 Linux 64位版本是物联网开发中的得力工具,它简化了ESP32设备的编程和调试流程,对于提升开发...

Global site tag (gtag.js) - Google Analytics