• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
20090222 getopt and_getopt_long
 

20090222 getopt and_getopt_long

on

  • 1,201 views

 

Statistics

Views

Total Views
1,201
Views on SlideShare
1,200
Embed Views
1

Actions

Likes
3
Downloads
6
Comments
0

1 Embed 1

http://www.slideshare.net 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    20090222 getopt and_getopt_long 20090222 getopt and_getopt_long Presentation Transcript

    • 程序输入参数的分析和处理 ---getopt 与 getopt_long 简介 colyli@gmail.com http://linuxfb.org
    • 内容 ● 容易忽略的繁琐细节 ● 手工分析参数 ● 参数输入格式约定 ● 借助 getopt 和 getopt_long ● 实例演示 ● 总结
    • 容易忽略的繁琐细节 最开始写的第一个程序往往是: void main(){ printf(“hello worldn”);} 然后不久,我们就会看到这样定义 main() 函数: int main(int argc, char *argv[]) 这时,输入参数的分析和处理,就不可避免的进入了我们的 视线。
    • 容易忽略的繁琐细节 参数哪里来 在 shell 中运行 > ./foo --bar 其中 --bar 就是程序 foo 的参数。 shell 将命令行内容通过 execve() 系统调用传递给内核: execve("./foo", ["./foo", "--bar"], [/* 59 vars */]) = 0 ●内核在处理该系统调用时,在完成了对 foo 程序的地址空 间分配后,会将参数字符串— bar 存放在 foo 程序地址空 间约定位置的页中。 ●foo 程序所链接的 libc 代码在 main() 函数被执行前,为 m ain() 函数初始化参数栈,即 argc 和 argv 。 ●调入 main() 函数时, argc 和 argv 就已经就绪可用了。
    • 容易忽略的繁琐细节 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[])
    • 容易忽略的繁琐细节 简单处理参数 int main(int argc, char *argv[]) { printf(“hello, %sn”, argv[1]); return 0; } > ./foo world hello, world > ./foo hell hello, hell > ./foo hello, (null) <--- ????
    • 容易忽略的繁琐细节 先别小看参数处理 ●在最开始学习编程时,精力会首先集中到算法实现中。 ●当真正开始要编写一个稍微实用一点点的程序时,在最初 并不会给予过多的重视,因此也不会去处理比较复杂的输 入参数。 ●或者往往要花费大量的精力来处理输入参数的种种细节, 也许整整一天时间,也无法编写出一个健壮的参数处理流 程。 ●最开始,参数处理是一个容易忽略的细节 ●然后,参数处理是一个非常繁琐的细节 ●当你不想它的时候,生活很美好 ●当你必须要处理参数的时候,这是一个无法忽略的、繁琐 的细节
    • 手工处理参数 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] ... ... }
    • 手工处理参数 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 ... } ... }
    • 手工处理参数 现在我的做法 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; } .... } 大量和字符串处理相关的细节可以抛在脑后了
    • GNU 参数输入格式约定 选项和参数 usage: foo -n [NAME] -s [male|female] -n 和 -s 是程序 foo 的输入选项 (option) [NAME] 是选项 -n 的参数 [male|female] 是选项 -s 的参数 选项并不一定非有参数 选项也可以具有多个参数 当选项数量太多时,就需要字符串的形式,诸如 --name 程序参数中也可以具有非选项参数, 例如下面命令中的 foo > ls ./foo
    • 参数输入格式约定 POSIX 参数格式约定 ● 如果程序参数以 '-' 开始,则为选项( option ) ●多个选项可以跟随在一个 '-' 后面。如 '-abc' 和 '-a -b -c' 效果相同。 ●选项名称可以是单个的字母或数字 ●某些选项可以具有自己的参数( ld -o output_file ) ●选项和参数之间不必要有空白隔开。如 '-o foo' 和 '-ofoo' 效果相同。 ●选项应当位于非选项参数之前。 ●参数 '--' 结束所有的选项。 '--' 之后的所有参数具备视为无 选项参数( non-option argument ) ●单一的 '-' 被当作是普通的无选项参数对待。一般会将它作为 标准输入输出。 ●选项可以以任何顺序出现多次,具体如何处理由程序自行决 定。
    • 参数输入格式约定 GNU 对长选项的格式约定 ● 长选项以 '--' 开始,后面跟随由字母、数字和 '-' 组成的字符串 ●用户可以同时使用长选项,或与之对应的短选项。譬如 'ls -a' 和 'ls --all' 的效果是相同的。 ●当需要为长选项指定参数时,可以使用 --name=value 的形 式。 OT :如何删除文件名以 '-' 起始的文件?
    • 借助 getopt 和 getopt_long 从简单开始 --getopt 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 (继续) 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 的额外细节(可以先跳过) 处理选项后跟随的参数的三种方法: (注意: '--' 参数后面,即使有 '-' 开头的参数,也不会被当作选 项,而作为非选项参数对待) 1) getopt 会将所有参数进行排序,将非选项参数排到最后。也 就是说用户输入参数的顺序,和 getopt 处理参数的顺序是 无关的。 2) 以 '-' 开头的选项参数将特殊对待。它们会被作为选项字 符 '1' 的参数返回 (不知道有什么用) 3) POSIX 要求遇到第一个非法选项时即停止参数处理。可以通 过设置环境变量 POSIXLY_CORRECT 或者在 optstring 开头添 加 '+' 来实现。
    • 借助 getopt 和 getopt_long 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_long getopt_long 并不复杂(继续) struct option { const char *name; int has_arg; int *flag; int val; }; name : '--' 后面的字符串 has_arg : no_argument, optional_argument, required_argument flag, val : – 当 flag 为 NULL 时,若 name 被匹配上,则 getopt_long 返回值 为 val 。 – 当 flag 指向一个 int 变量的地址时,若选项 name 被匹配上, 则 getopt_long 返回 0 ,并将 val 的数值存储在 flag 指向的地 址中。
    • 借助 getopt 和 getopt_long getopt_long 并不复杂(继续) longopts : – 由描述长选项的 struct option 结构组成的数组。 – 最后一个元素以 {0,0,0,0} 结尾。 longindex : – 当一个长选项被匹配上时, longindex 可以用来索引当前被匹配 中的选项结构: longopts[*longindex].name – 当长选项没有匹配中时, longindex 为 NULL 。
    • 借助 getopt 和 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 很酷的 getsubopt int getsubopt(char **optionp, char * const * tokens, char **valuep); optionp : ● 需要处理的参数字串。在没有处理子选项之前,即为 optarg 的值。 tokens : ● 合法的子选项列表。定义方法见实例代码。 valuep : ● 当子选项中出现 name=value 的参数时, valuep 指向 value 字 符串。 返回值: ● 当匹配某个子选项后,返回该子选项在 tokens 中的索引数 值。
    • 实例演示 找朋友程序 find_friend 假定我们想通过一个名为 find_friend 的命令行程序来寻找符合某些 查询条件的男性或女性朋友。 可用的查询条件有: 年龄:确定数值 体重:确定数值 特性:甜蜜的微笑,勤劳做家务,聪慧,苗条 工作:可选的工作描述 梦中情人:被查询者的梦中情人的: - 名字:确定字符串 - 体重:确定数字 - 身高:确定数字 范围:是否只在本校内寻找
    • 实例演示 找朋友程序 find_friend (继续) 下面是 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 (继续) 请参看实例代码 find_friend.c 也可以从如下地址下载到: http://www.mlxos.org/docs/find_friend.c
    • 总结 不要低估工具的力量 正确的工具,可以使我们在工作中事半功倍 要熟悉 GNU 中的各种强大的工具
    • Thank you ! Happy Hacking !