转载请注明出处.
问题引入----命令行參数及解析
在使用linux时,与windows最大的不同应该就是常常使用命令行来解决大多数问题.比方以下这种:
而显然我们知道C语言程序的入口是mian函数,即是从main函数開始运行,而main函数的原型是:
int main( int argc, char *argv[] );int main( int argc, char **argv );
程序的 main 函数能够通过參数 argc 和 argv 来訪问程序的參数列表(假设你不须要訪问參数列表,你能够直接忽略它们)。
第一个參数 argc 指示了命令行中參数的数量(这个值包括命令本身,假设后面没有參数,则值为1)。
第二个參数 argv 是一个字符串数组。
其各个成员分别指向各个參数,即argv[0]指向命令本身,详细见下图.数组的大小由 argc 指定,而数组的元素则为各个命令行參数
的元素,表示以 NULL 结束的字符串形式。 使用命令行參数的过程因此被简化为检查 argc 和 argv 的内容。假设你对程序自己的名 字没有兴趣,记得跳过第一个參数.以下的程序演示使用 argc 和 argv 的方法:
/************************************************************************* > File Name: arglist.c > Author: suool > Mail: 1020935219@qq.com > Created Time: 2014年07月24日 星期四 16时35分40秒 ************************************************************************/ #includeint main (int argc, char* argv[]){ printf ("The name of this program is '%s'.\n", argv[0]); printf ("This program was invoked with %d arguments.\n", argc - 1); /* 指定了命令行參数么?
*/ if (argc > 1) { /* 有,那么输出这些參数。*/ int i; printf ("The arguments are:\n"); for (i = 1; i < argc; ++i) printf (" %s\n", argv[i]); } return 0; }
结果例如以下:差点儿全部 GNU/Linux 程序都遵循一些处理命令行參数的习惯。程序期望得到的參数能够被分为两种: 选项( options ,又作 flags ) 和其他(非选项)參数。选项用于调整程序的 行为方式,而其他參数提供了程序的输入(比如,输入文件的名字)。
选项通常有两种格式: · 短选项( short options ) 由一个短杠(hyphen)和一个字符(通常为一个大写或小写字母)组成。短选项能够方便用户的输入。 · 长选项( long options ) 由两个短杠開始,之后尾随一个由大写和小写字母和短杠组成的名字。
长选项方便记忆和阅读(尤其在脚本中使用的时候)
通常程序会为它支持的选项提供长短两种形式,前者为便于用户理解,而后者为简化输入。比如,多数程序都能理解 –h 和 –help 这两个參数,并以同样方法进行处理。一般而 言,当从 shell 中调用一个程序的时候,程序名后紧跟的就是选项參数。假设一些选项须要一个參数,则參数紧跟在选项之后。比如,很多程序将选项 –output foo 解释为“将输出文 件设置为 foo”。在选项之后可能出现其他命令行參数,通经常使用于指明输入文件或输入数据。
比如,命令行 ls –s / 列举根文件夹的内容。选项 –s 改变了 ls 的默认行为方式,通知它为每一个条目显示文件大小(以 KB 为单位)。
參数 / 向 ls 指明了被列举的文件夹。
选项 –size 与–s 选项具有同样的含义,因此调用 ls –size / 会得到全然同样的结果。
这时候我们就会发现一个问题,那就是假设參数比較多,并且要有一定的顺序怎么办?
怎样解析命令行的參数,推断參数的位置和正确与否呢?
这就是这次要讲的东西.
命令行參数识别
简单命令行处理getopt获取并解析命令行參数
getopt()
函数位于 unistd.h 系统头文件里。其原型如示: int getopt( int argc, char *const argv[], const char *optstring );
给定了命令參数的数量 (argc
)、指向这些參数的数组 (argv
) 和选项字符串 (optstring
) 后,getopt()
将返回第一个选项,并设置一些全局变量。使用同样的參数再次调用该函数时,它将返回下一个选项。并设置对应的全局变量。假设不再有识别到的选项,将返回 -1
。此任务就完毕了。
getopt()
所设置的全局变量包含:
optarg
——指向当前选项參数(假设有)的指针。optind
——再次调用getopt()
时的下一个 argv 指针的索引。optopt
——最后一个已知选项。
对于每一个选项,选项字符串 (optstring
) 中都包括一个相应的字符。
具有參数的选项后面跟有一个 :
字符。
当中optstrings 能够使以下的元素:
- 1.单个字符,表示选项
- 2.单个字符后面一个冒号:表示该选项后面必须跟一个參数.參数紧跟在选项后面或者以空格隔开,该參数的指针赋给optarg.
- 3.单个字符后面跟两个冒号:: ,表示该选项后面能够跟一个參数.參数必须紧跟在选项后面不能以空格隔开.该參数一样赋给optarg.
如,假设opstrings = "ab:c::d::",命令行參数例如以下:
./getopt -a -b host -chello -d world.这个命令行參数中,去掉短參数的-,当中a,b,c就是选项,host是b的參数,hello是c的參数,可是world不是d的參数,由于有空格隔开.
能够反复调用 getopt()
,直到其返回 -1
为止;不论什么剩下的命令行參数通常视为文件名称或程序对应的其它内容。
以下我们看两个演示样例程序.
1.有一个程序有三个选项a,b,c,当中b必须带參数,而c能够带能够不带.请写一个程序使用getopt来解析这个程序的命令行參数.
以下是代码演示样例:
#include#include #include int main(int argc, char **argv){ int result; opterr = 0; // 不输出错误信息 while( (result = getopt(argc, argv, "ab:c::")) != -1 ) { // 一直解析 switch(result) { case 'a': printf("option=a, optopt=%c, optarg=%s\n", optopt, optarg); break; case 'b': printf("option=b, optopt=%c, optarg=%s\n", optopt, optarg); break; case 'c': printf("option=c, optopt=%c, optarg=%s\n", optopt, optarg); break; case '?
': printf("result=?
, optopt=%c, optarg=%s\n", optopt, optarg); break; default: printf("default, result=%c\n",result); break; } printf("argv[%d]=%s\n", optind, argv[optind]); } printf("result=-1, optind=%d\n", optind); // 打印最后有可能出错的位置 for(result = optind; result < argc; result++) // 打印余下的错误选项 printf("-----argv[%d]=%s\n", result, argv[result]); for(result = 1; result < argc; result++) // 打印又一次排列的选项列表 printf("\nat the end-----argv[%d]=%s\n", result, argv[result]); return 0; }
执行结果例如以下:第一次命令行參数为:
./getopt_exp -a host -b hello -cworld -d解析的结果为:
其它的请自己尝试吧,能够尝试一下没有错误的,或者更加错误的,
2.一个假想的 doc2html 程序的命令行处理。
该 doc2html 程序将某种类型的文档转换为 HTML,详细由用户指定的命令行选项控制。它支持下面选项:
-I
——不创建keyword索引。-l lang
——转换为使用语言代码lang
指定的语言。-o outfile.html
——将经过转换的文档写入到 outfile.html,而不是打印到标准输出。-v
——进行转换时提供具体信息;能够多次指定,以提高诊断级别。- 将使用其它文件名来作为输入文档。
还将支持 -h
和 -?
。以打印帮助消息来提示各个选项的用途。
/* getopt_demo - demonstrate getopt() usage * * This application shows you one way of using getopt() to * process your command-line options and store them in a * global structure for easy access. */#include#include #include /* doc2html supports the following command-line arguments: * * -I - don't produce a keyword index * -l lang - produce output in the specified language, lang * -o outfile - write output to outfile instead of stdout * -v - be verbose; more -v means more diagnostics * additional file names are used as input files * * The optString global tells getopt() which options we * support, and which options have arguments. */struct globalArgs_t { int noIndex; /* -I option */ char *langCode; /* -l option */ const char *outFileName; /* -o option */ FILE *outFile; int verbosity; /* -v option */ char **inputFiles; /* input files */ int numInputFiles; /* # of input files */} globalArgs;static const char *optString = "Il:o:vh?";/* Display program usage, and exit. */void display_usage( void ){ puts( "doc2html - convert documents to HTML" ); /* ... */ exit( EXIT_FAILURE );}/* Convert the input files to HTML, governed by globalArgs. */void convert_document( void ){ /* ... */}int main( int argc, char *argv[] ){ int opt = 0; /* Initialize globalArgs before we get to work. */ globalArgs.noIndex = 0; /* false */ globalArgs.langCode = NULL; globalArgs.outFileName = NULL; globalArgs.outFile = NULL; globalArgs.verbosity = 0; globalArgs.inputFiles = NULL; globalArgs.numInputFiles = 0; /* Process the arguments with getopt(), then * populate globalArgs. */ opt = getopt( argc, argv, optString ); while( opt != -1 ) { switch( opt ) { case 'I': globalArgs.noIndex = 1; /* true */ break; case 'l': globalArgs.langCode = optarg; break; case 'o': /* This generates an "assignment from * incompatible pointer type" warning that * you can safely ignore. */ globalArgs.outFileName = optarg; break; case 'v': globalArgs.verbosity++; break; case 'h': /* fall-through is intentional */ case '?
': display_usage(); break; default: /* You won't actually get here. */ break; } opt = getopt( argc, argv, optString ); } globalArgs.inputFiles = argv + optind; globalArgs.numInputFiles = argc - optind; convert_document(); return EXIT_SUCCESS; }
以下分解上面的代码:头文件:
#include#include #include
创建的 globalArgs
结构,用于以合理的方式存储命令行选项。由于这是个全局变量,程序中不论什么位置的代码都能够訪问这些变量。以确定是否创建keyword索引、生成何种语言等等事项。最好让 main()
函数外的代码将此结构视为一个常量、仅仅读存储区。由于程序的不论什么部分都能够依赖于其内容。每一个命令行选择都有一个相应的选项,而其它变量用于存储输出文件名称、指向输入文件列表的指针和输入文件数量。
struct globalArgs_t { int noIndex; /* -I option */ char *langCode; /* -l option */ const char *outFileName; /* -o option */ FILE *outFile; int verbosity; /* -v option */ char **inputFiles; /* input files */ int numInputFiles; /* # of input files */} globalArgs;static const char *optString = "Il:o:vh?";
选项字符串 optString
告知 getopt()
能够处理哪个选项以及哪个选项须要參数。
假设在处期间遇到了其它选项,getopt()
将显示一个错误消息,程序将在显示了用法消息后退出。
以下的代码段包括一些从 main()
引用的使用方法消息函数和文档转换函数的小存根。
能够对这些存根进行自由更改,以用于更为实用的目的。
void display_usage( void ){ puts( "doc2html - convert documents to HTML" ); /* ... */ exit( EXIT_FAILURE );}void convert_document( void ){ /* ... */}最后,以下的代码段中所看到的。在
main()
函数中使用此结构。和优秀的开发者一样,须要首先初始化 globalArgs
结构,然后才開始处理命令行參数。在程序中。能够借此设置在一定情况下合理的缺省值。以便在以后有更合适的缺省值时更方便地对其进行调整
int main( int argc, char *argv[] ){ int opt = 0; /* Initialize globalArgs before we get to work. */ globalArgs.noIndex = 0; /* false */ globalArgs.langCode = NULL; globalArgs.outFileName = NULL; globalArgs.outFile = NULL; globalArgs.verbosity = 0; globalArgs.inputFiles = NULL; globalArgs.numInputFiles = 0;以下的代码段中的
while
循环和 switch
语句是用于本程序的命令行处理的代码部分。仅仅要 getopt()
发现选项。switch
语句将确定找到的是哪个选项,将能在 globalArgs
结构中看到详细情况。当 getopt()
终于返回 -1
时,就完毕了选项处理过程,剩下的都是输入文件了。 opt = getopt( argc, argv, optString ); while( opt != -1 ) { switch( opt ) { case 'I': globalArgs.noIndex = 1; /* true */ break; case 'l': globalArgs.langCode = optarg; break; case 'o': globalArgs.outFileName = optarg; break; case 'v': globalArgs.verbosity++; break; case 'h': /* fall-through is intentional */ case '?
': display_usage(); break; default: /* You won't actually get here. */ break; } opt = getopt( argc, argv, optString ); } globalArgs.inputFiles = argv + optind; globalArgs.numInputFiles = argc - optind;
既然已经完毕了參数和选项的收集工作,接下来就能够运行程序所设计的不论什么功能(在本例中是进行文档转换)。然后退出
convert_document(); return EXIT_SUCCESS;}当然上面的使用optget是简单的命令行处理,假设你要进行长參数的或者略微复杂的处理,那就继续往下看吧.
getopt_long()
是同一时候支持长选项和短选项的getopt()
版本号。 复杂的命令行处理getopt获取并解析命令行參数
getlongopt的原型是:
int getopt_long (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *indexptr)第一个參数是当前传递进来的參数个数,第二个參数是当前传递进来的參数列表,第三个參数是当前进程全部可支持的短參数的字符串,第四个參数是struct option,表示全部的长參数的相应关系.
结构体声明例如以下:
struct option { # if (defined __STDC__ && __STDC__) || defined __cplusplus const char *name; # else char * name; # endif int has_arg; // 该參数是否须要带參数 int *flag; // 标志 int val; // 返回參数值};
name 成员是指向长选项名称(带两个短横线)的指针。has_arg 成员设置为 no_argument、optional_argument, 或 required_argument(均在 getopt.h 中定义)之中的一个。以指示选项是否具有參数。假设 flag 成员未设置为 NULL。在处理期间遇到此选项时。会使用 val 成员的值填充它所指向的 int 值。假设 flag 成员为 NULL。在 getopt_long() 遇到此选项时,将返回 val 中的值;通过将 val 设置为选项的 short 參数,能够在不加入不论什么其它代码的情况下使用 getopt_long()——处理 while loop 和 switch 的现有 getopt() 将自己主动处理此选项。
这已经变得更为灵活了,由于各个选项如今能够具有可选參数了。更重要的是。仅须要进行非常少的工作,就能够方便地放入现有代码中。 此函数的返回情况例如以下:1.在使用此函数处理一个參数时,全局变量optarg指向下一个要处理的变量,并返回struct option的第四个成员.普通情况下,假设struct option的第三个參数位置设置null,第四个參数一般设置为该长选项相应的短选项的字符值,即返回相应的短选项字符.
2.假设解析完最后一个成员将返回1
3.假设getlongopt遇到一个错误的选项,他将打印一个错误消息并返回'?'
4.当get_long解析一个长选项而且发现后面没有參数则返回':',表示缺少參数.
应用举例.
1.一个程序的所需的短选项和长选项例如以下:
短选项 长选项 作用-h --help 输出程序的命令行參数说明并退出-o filename --output filename 给定输出文件名称-v --version 显示程序当前的版本号后退出在这个程序中,首先须要确定两个结构:
1,一个字符串,包括所须要的短选项字符,假设选项后面有參数,字符后面加一个冒号:.本例中,这个字符串应该是:"ho:v",由于-o后面有參数filename,所以要加冒号.
2,一个包括长选项的结构体数组.每一个结构体四个域.
第一个域为长选项字符串.第二个域为表示对应的选项是否需要參数,仅仅能为0,没有參数,1,必需要,2,能够要.第三个域决定返回结果类型(即是flags,建议设null),假设为null,则此函数将返回后一个域(即val,一般设置为当前获取的短參数值),否则,此函数将返回0.第四个域为函数的返回值,一般设置为对用的短选项的ascii值.
另外,结构体数组的最后一个元素所有设置为null和0,表示结束.
结构体能够例如以下所看到的:
// 描写叙述了长选项的 struct option 数组 const struct option long_options[] = { { "help", 0, NULL, 'h' }, { "output", 1, NULL, 'o' }, { "version", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } // 数组末要求这样一个元素。 };下面是程序的代码:
#include#include #include #include const char* program_name; //the filenamevoid print_usage (FILE* stream, int exit_code) //output message{ fprintf (stream, "Usage: %s options [ inputfile ... ]\n", program_name); fprintf ( stream, " -h --help .\n" " -o --output filename.\n" " -v --version.\n" ); exit (exit_code);}// mainint main (int argc, char* argv[]){ int next_option; const char* const short_options = "ho:v"; // list of short options const struct option long_options[] = // structure of long options { { "help", 0, NULL, 'h' }, //no arg { "output",1, NULL, 'o' }, //must have a arg { "version",0, NULL, 'v' }, { NULL, 0, NULL, 0} // necessary ele }; const char* output_filename = NULL; program_name = argv[0]; do { next_option = getopt_long (argc, argv, short_options, long_options, NULL); switch (next_option) { case 'h': //-h or --help, print the info of help print_usage (stdout, 0); break; case 'o': // -o or --output print the content of the file output_filename = optarg; execl("/bin/cat","cat",output_filename,NULL); break; case 'v': //-v or --version printf("the version is v1.0\n"); break; case ':': break; case '?
': print_usage (stderr, 1); // unknow break; default: // else print_usage (stderr, 1); break; } }while (next_option !=-1); return 0; }
以下是測试结果:1.命令行:
./getopt_long_exp -help
./getopt_long_exp -v
./getopt_long_exp --version
./getopt_long_exp --output getopt_long_exp.c
2.对上面的转换程序的改动:
getopt_long()
函数在 getopt.h
头文件(而非 unistd.h
)中,因此将须要将该头文件包括进来。我还包括了 string.h
。由于将稍后使用 strcmp()
来帮助确定处理的是哪个长參数。
其它头文件
#include#include
已经为 --randomize
选项在 globalArgs
中加入了一个标志。并创建了 longOpts
数组来存储关于此程序支持的长选项的信息。除了 --randomize
外。全部的參数都与现有短选项相应(比如,--no-index
等同于 -I
)。通过在选项结构中包括其短选项等效项,能够在不向程序加入不论什么其它代码的情况下处理等效的长选项。
扩展后的參数
opt = getopt_long( argc, argv, optString, longOpts, &longIndex ); while( opt != -1 ) { switch( opt ) { case 'I': globalArgs.noIndex = 1; /* true */ break; case 'l': globalArgs.langCode = optarg; break; case 'o': globalArgs.outFileName = optarg; break; case 'v': globalArgs.verbosity++; break; case 'h': /* fall-through is intentional */ case '?': display_usage(); break; case 0: /* long option without a short arg */ if( strcmp( "randomize", longOpts[longIndex].name ) == 0 ) { globalArgs.randomized = 1; } break; default: /* You won't actually get here. */ break; } opt = getopt_long( argc, argv, optString, longOpts, amp;longIndex ); }
将 getopt()
调用更改为了 getopt_long()
。除了 getopt()
的參数外,它还接受 longOpts
数组和 int
指针 (longIndex
)。当getopt_long()
返回 0
时。longIndex
所指向的整数将设置为当前找到的长选项的索引。
新的经改进的选项处理
opt = getopt_long( argc, argv, optString, longOpts, &longIndex ); while( opt != -1 ) { switch( opt ) { case 'I': globalArgs.noIndex = 1; /* true */ break; case 'l': globalArgs.langCode = optarg; break; case 'o': globalArgs.outFileName = optarg; break; case 'v': globalArgs.verbosity++; break; case 'h': /* fall-through is intentional */ case '?': display_usage(); break; case 0: /* long option without a short arg */ if( strcmp( "randomize", longOpts[longIndex].name ) == 0 ) { globalArgs.randomized = 1; } break; default: /* You won't actually get here. */ break; } opt = getopt_long( argc, argv, optString, longOpts, amp;longIndex ); }
我还加入了 0
的 case,以便处理不论什么不与现有短选项匹配的长选项。在此例中,仅仅有一个长选项。但代码仍然使用 strcmp()
来确保它是预期的那个选项。
这样就所有搞定了;程序如今支持更为具体(对暂时用户更加友好)的长选项。
Summarize
UNIX 用户始终依赖于命令行參数来改动程序的行为。特别是那些设计作为小工具集合 (UNIX 外壳环境)的一部分使用的有用工具更是如此。
程序须要可以高速处理各个选项和參数,且要求不会浪费开发者的太多时间。毕竟,差点儿没有程序设计为仅处理命令行參数,开发者更应该将精力放在程序所实际进行的工作上。
getopt()
函数是一个标准库调用,可同意使用直接的 while/switch 语句方便地逐个处理命令行參数和检測选项(带或不带附加的參数)。与其类似的 getopt_long()
同意在差点儿不进行额外工作的情况下处理更具描写叙述性的长选项,这很受开发者的欢迎。
既然已经知道了怎样方便地处理命令行选项,如今就能够集中精力改进程序的命令行,能够加入长选项支持。或加入之前因为不想向程序加入额外的命令行选项处理而搁置的不论什么其它选项。
不要忘记在某处记录全部的选项和參数,并提供某种类型的内置帮助函数来为健忘的用户提供帮助。
reference:
linux高级程序设计 杨宗德
http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html
转载请注明出处.http://blog.csdn.net/suool/article/details/38089001
1. ANSI C文件I/O管理
2. POSIX文件以及文件夹管理
3. Linux下的一些编码特性和规范
4. Python 两大网络框架
版权声明:本文博客原创文章,博客,未经同意,不得转载。