20090222 getopt and_getopt_long

26
程序输入参数的分析和处理 ---getopt 与 getopt_long 简介 [email protected] http://linuxfb.org

Upload: ziming-hu

Post on 12-May-2015

1.092 views

Category:

Technology


3 download

TRANSCRIPT

Page 1: 20090222 getopt and_getopt_long

程序输入参数的分析和处理---getopt 与 getopt_long 简介

[email protected]

http://linuxfb.org

Page 2: 20090222 getopt and_getopt_long

内容

● 容易忽略的繁琐细节● 手工分析参数● 参数输入格式约定● 借助 getopt 和 getopt_long● 实例演示● 总结

Page 3: 20090222 getopt and_getopt_long

容易忽略的繁琐细节

最开始写的第一个程序往往是:void main(){ printf(“hello world\n”);}

然后不久,我们就会看到这样定义 main() 函数:int main(int argc, char *argv[])

这时,输入参数的分析和处理,就不可避免的进入了我们的视线。

Page 4: 20090222 getopt and_getopt_long

容易忽略的繁琐细节参数哪里来

在 shell 中运行 > ./foo --bar其中 --bar 就是程序 foo 的参数。 shell 将命令行内容通过execve() 系统调用传递给内核: execve("./foo", ["./foo", "--bar"], [/* 59 vars */]) = 0execve("./foo", ["./foo", "--bar"], [/* 59 vars */]) = 0

●内核在处理该系统调用时,在完成了对 foo 程序的地址空间分配后,会将参数字符串— bar 存放在 foo 程序地址空间约定位置的页中。

●foo 程序所链接的 libc 代码在 main() 函数被执行前,为 main() 函数初始化参数栈,即 argc 和 argv 。

●调入 main() 函数时, argc 和 argv 就已经就绪可用了。

Page 5: 20090222 getopt and_getopt_long

容易忽略的繁琐细节argc,argv

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

argc :命令行参数的个数,包括程序名本身,在前例中,数值为 2

argv :由 C语言中的字符串组成的数组(向量),每一个元素对应一个命令行参数。被运行程序的名称是该向量的第一个元素,即 argv[0] 。 虽然 argv[argc] 没有定义,但实际中总被设置为 NULL 。

main() 还有一种格式如下,但这里不涉及:int main(int argc, char *argv[], char *envp[])

Page 6: 20090222 getopt and_getopt_long

容易忽略的繁琐细节简单处理参数

int main(int argc, char *argv[]){ printf(“hello, %s\n”, argv[1]); return 0;}

> ./foo world hello, world> ./foo hell hello, hell> ./foo hello, (null) <--- ????

Page 7: 20090222 getopt and_getopt_long

容易忽略的繁琐细节先别小看参数处理

●在最开始学习编程时,精力会首先集中到算法实现中。●当真正开始要编写一个稍微实用一点点的程序时,在最初并不会给予过多的重视,因此也不会去处理比较复杂的输入参数。

●或者往往要花费大量的精力来处理输入参数的种种细节,也许整整一天时间,也无法编写出一个健壮的参数处理流程。

●最开始,参数处理是一个容易忽略的细节●然后,参数处理是一个非常繁琐的细节●当你不想它的时候,生活很美好●当你必须要处理参数的时候,这是一个无法忽略的、繁琐的细节

Page 8: 20090222 getopt and_getopt_long

手工处理参数9 年前我的做法

usage: foo -n [NAME] -s [male|female]

main(... ...){ if (argc != 5) {print error and exit} if (!strcmp(argv[1], “-n”)) name_str = argv[2]; else if (!strcmp(argv[1], “-s”)) sex_str = argv[2] ... ...

}

Page 9: 20090222 getopt and_getopt_long

手工处理参数4 年前我的做法

usage: foo -n [NAME] -s [male|female]

while (1){ if (!strcmp(argv[index], “-n”)) { index ++; name_str = argv[index]; continue; } else if (!strcmp(argv[index], “-s”)) { parse argument for -s option ... } ...}

Page 10: 20090222 getopt and_getopt_long

手工处理参数现在我的做法

usage: foo -n [NAME] -s [male|female]

while ((c = getopt(argc, argv, “n:s:”)) != -1){ switch (c) { case 'n': name_str = optarg; break; case 's': sex_str = optarg; break; default: error and exit; } ....}大量和字符串处理相关的细节可以抛在脑后了

Page 11: 20090222 getopt and_getopt_long

GNU 参数输入格式约定选项和参数

usage: foo -n [NAME] -s [male|female]

-n 和 -s 是程序 foo 的输入选项 (option)[NAME] 是选项 -n 的参数[male|female] 是选项 -s 的参数

选项并不一定非有参数选项也可以具有多个参数当选项数量太多时,就需要字符串的形式,诸如 --name

程序参数中也可以具有非选项参数, 例如下面命令中的 foo > ls ./foo

Page 12: 20090222 getopt and_getopt_long

●如果程序参数以 '-'开始,则为选项( option )●多个选项可以跟随在一个 '-'后面。如 '-abc'和 '-a -b -c' 效果相同。

●选项名称可以是单个的字母或数字●某些选项可以具有自己的参数( ld -o output_file )●选项和参数之间不必要有空白隔开。如 '-o foo'和 '-ofoo' 效果相同。

●选项应当位于非选项参数之前。●参数 '--'结束所有的选项。 '--' 之后的所有参数具备视为无选项参数( non-option argument )

●单一的 '-'被当作是普通的无选项参数对待。一般会将它作为标准输入输出。

●选项可以以任何顺序出现多次,具体如何处理由程序自行决定。

参数输入格式约定POSIX 参数格式约定

Page 13: 20090222 getopt and_getopt_long

●长选项以 '--'开始,后面跟随由字母、数字和 '-'组成的字符串●用户可以同时使用长选项,或与之对应的短选项。譬如 'ls -a'和 'ls --all'的效果是相同的。

●当需要为长选项指定参数时,可以使用 --name=value 的形式。

OT:如何删除文件名以 '-' 起始的文件?

参数输入格式约定GNU 对长选项的格式约定

Page 14: 20090222 getopt and_getopt_long

int getopt(int argc, char * const argv[], const char *optstring)

argc, argv: 来自 main(argc, argv)optstring: 描述选项字符的字符串 1) 每一个字符是一个短选项 2) 如果该选项要求参数,则在字母后追加 ':' 3) 如果该选项的参数是可选的,则在字母后追加 '::'

返回值: 1) 如果找到某个选项,则返回这个选项对应的字符 2) 如果分析完了所有的选项(注意不是参数注意不是参数),则返回 -1 3) 如果遇到无法匹配的选项,则返回 '?'

借助 getopt 和 getopt_long从简单开始 --getopt

Page 15: 20090222 getopt and_getopt_long

extern int opterr: 如果该变量不为 0,则 getopt 在遇到不合法选项时会打印错误信息到 stderr 。

extern int optopt:当遇到不合法的选项字符时, optopt 即被赋值为该不合法选项字符。

extern char *optarg :当匹配一个选项后,如果该选项带参数,则 optarg指向参数字串;若该选项不带参数,则 optarg 为 NULL;若该选项的参数为可选时, optarg 为 NULL表明无参数, optarg 不为 NULL 时则指向输入参数字串。

extern int optind :当 getopt返回时,指向 argv 中下一个待处理索引的值。可以用来检测是否所有参数全部都处理完毕。

借助 getopt 和 getopt_long从简单开始 --getopt (继续)

Page 16: 20090222 getopt and_getopt_long

处理选项后跟随的参数的三种方法:(注意: '--' 参数后面,即使有 '-'开头的参数,也不会被当作选项,而作为非选项参数对待)1) getopt 会将所有参数进行排序,将非选项参数排到最后。也

就是说用户输入参数的顺序,和 getopt 处理参数的顺序是无关的。

2) 以 '-'开头的选项参数将特殊对待。它们会被作为选项字符 '\1'的参数返回 (不知道有什么用)

3) POSIX要求遇到第一个非法选项时即停止参数处理。可以通过设置环境变量 POSIXLY_CORRECT或者在 optstring 开头添加 '+'来实现。

借助 getopt 和 getopt_longgetopt 的额外细节(可以先跳过)

Page 17: 20090222 getopt and_getopt_long

getopt() 函数只能处理以 '-'开头的单个字符标识的选项

getopt_long 可以处理以 '--'开头的字符串标识的选项,并同时接受getopt() 可以处理的单字符选项。

int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);

argc, argv, optstring 定义与 getopt()相同。增加了 struct option longopts 和 int *longindex 。

借助 getopt 和 getopt_longgetopt_long 并不复杂

Page 18: 20090222 getopt and_getopt_long

struct option { const char *name; int has_arg; int *flag; int val;};

name : '--'后面的字符串has_arg : no_argument, optional_argument, required_argumentflag, val :

– 当 flag 为 NULL 时,若 name 被匹配上,则 getopt_long返回值为 val 。

– 当 flag指向一个 int变量的地址时,若选项 name 被匹配上,则 getopt_long返回 0 ,并将 val 的数值存储在 flag指向的地址中。

借助 getopt 和 getopt_longgetopt_long 并不复杂(继续)

Page 19: 20090222 getopt and_getopt_long

longopts :– 由描述长选项的 struct option 结构组成的数组。– 最后一个元素以 {0,0,0,0} 结尾。

longindex :– 当一个长选项被匹配上时, longindex 可以用来索引当前被匹配

中的选项结构: longopts[*longindex].name– 当长选项没有匹配中时, longindex 为 NULL 。

借助 getopt 和 getopt_longgetopt_long 并不复杂(继续)

Page 20: 20090222 getopt and_getopt_long

有时候一个选项中还可能存在多个自选项,譬如 mount 命令中的 -o参数中的选项列表:mount -t iso9660 -o ro,user,noauto,unhide /dev/cdrom /cd上述命令中, -o选项后面包含了 4个子选项。

处理类似的子选项时,可以使用 getsubopt() 函数:

#define _XOPEN_SOURCE 500#include <stdlib.h>

int getsubopt(char **optionp, char * const * tokens, char **valuep);

借助 getopt 和 getopt_long选项参数中的子选项

Page 21: 20090222 getopt and_getopt_long

int getsubopt(char **optionp, char * const * tokens, char **valuep);

optionp :● 需要处理的参数字串。在没有处理子选项之前,即为 optarg的值。

tokens :● 合法的子选项列表。定义方法见实例代码。valuep :● 当子选项中出现 name=value 的参数时, valuep指向 value 字符串。

返回值:● 当匹配某个子选项后,返回该子选项在 tokens 中的索引数值。

借助 getopt 和 getopt_long很酷的 getsubopt

Page 22: 20090222 getopt and_getopt_long

假定我们想通过一个名为 find_friend 的命令行程序来寻找符合某些查询条件的男性或女性朋友。

可用的查询条件有:年龄:确定数值体重:确定数值特性:甜蜜的微笑,勤劳做家务,聪慧,苗条工作:可选的工作描述梦中情人:被查询者的梦中情人的: - 名字:确定字符串 - 体重:确定数字 - 身高:确定数字范围:是否只在本校内寻找

实例演示找朋友程序 find_friend

Page 23: 20090222 getopt and_getopt_long

下面是 find_friend 对命令行参数的定义:

Usage: find_friend [OPTION]... [male|female]Search boy/girl friends with your requirement.

Here are options for your query-a, --age exact age-w, --weight exact weight-c, --character personal characters, please separate each option with comma sweet always sweet smile laborious happy to do housework smart of cause not too smart then you slim perfect shape in common sense-e, --employed if she or he has a job <job name> optional, if you also want to search her or his job-d, --dreamlover extra conditions if she or he has dream- lover, separate each suboption with comma name=<name> name of dreamlover weight=<weight> weight of dreamlover height=<height> height of dreamlover-l, --local only search your own school-f, --flagtest flag test only

实例演示找朋友程序 find_friend (继续)

Page 24: 20090222 getopt and_getopt_long

请参看实例代码 find_friend.c

也可以从如下地址下载到:http://www.mlxos.org/docs/find_friend.c

实例演示找朋友程序 find_friend (继续)

Page 25: 20090222 getopt and_getopt_long

总结

不要低估工具的力量正确的工具,可以使我们在工作中事半功倍要熟悉 GNU 中的各种强大的工具

Page 26: 20090222 getopt and_getopt_long

Thank you !

Happy Hacking !