应用场景
命令行参数有不同的样式。
# 短选项与长选项ls -a# 短线后面跟选项,选项没有参数ls -alh# 不带参数的选项可用连写,不分先后顺序ls --all# 双短线的长选项,和-a是相同的功能# 选项后面是否跟参数ls -a# 不带参数ls --block-size=SIZE# 带参数ls --color[=WHEN]# 参数可选,选项和参数之间没有空格
ls使用getopt_long()函数实现上面功能。
staticstructoptionconst long_options[]={{"all", no_argument,NULL,'a'},{"escape", no_argument,NULL,'b'},...{"context", no_argument,0,'Z'},{"author", no_argument,NULL, AUTHOR_OPTION},{GETOPT_HELP_OPTION_DECL},{GETOPT_VERSION_OPTION_DECL},{NULL,0,NULL,0}};int c=getopt_long(argc, argv,"abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UXZ1",long_options,&oi);
可以看到命令行选项有两种样式(两者不等价):long_options[]
和"abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UXZ1"
。
(对于--color长选项来说,它没有对应的短选项)
long_options[]
使用的option
结构体如下:
structoption{constchar*name;// 长选项名int has_arg;// 选项是否带参数:不带参数,带参数,参数可选int*flag;// 当flag等于NULL,getopt_long返回valint val;// val 可以设置成长选项名的第一个字母};
长选项转换成短选项。
constchar*optstring_from_long_options(conststructoption*opt){// 将长参数转换成短参数// "AB:C::"表示:A后面没有参数;B后面有参数;C后面可以有参数,也可以没有,有的话必须紧跟选项staticchar optstring[256]={0};char*osp= optstring;for(; opt->name!=NULL; opt++){if(opt->flag==0&& opt->val>='A'&& opt->val<='z'){*osp++= opt->val;switch(opt->has_arg){case optional_argument:*osp++=':';*osp++=':';break;case required_argument:*osp++=':';break;default:break;}}}return optstring;}
实践
下面的代码改自fuzznetlink.c。
需求:打印文字情感logo。
- 一个选项(无参数),用以确定情感文字类型。
- 一个选项(带参数),用以确定打印一类情感文字的个数。
- 一个选项(可选参数),不带参数时随机打印一个情感文字,带参数时打印指定个数的情感文字。
➜ tree.
├── comm.h
├── logo.cpp
├── long_options.cpp
└── Makefile0 directories,4 files
其他代码见仓库。下面为命令行参数处理代码。
#include<getopt.h>#include<iostream>#include<vector>#include<string>#include"comm.h"
using namespace std;structstate{int smile_nums=0;int cry_nums=0;int tired_nums=0;int nums=0;int kinds=3;};staticvoidusage(constchar*command){// C语言中连续的用""引起的字符串常量,会默认合并为一个常量字符串fprintf(stderr,"Usage:\n""\n"" %s [option]\n""\n""Options;\n""\n"" -s --smile Print smile logo\n"" -c --cry Print cry logo\n"" -t --tired Print tired logo\n"" -n --nums nums logos are printed\n"" -r --rand rand(choice) output logo\n""\n",
command);}constchar*optstring_from_long_options(conststructoption*opt){// 将长参数转换成短参数// "AB:C::"表示:A后面没有参数;B后面有参数;C后面可以有参数,也可以没有,有的话必须紧跟选项staticchar optstring[256]={0};char*osp= optstring;for(; opt->name!=NULL; opt++){if(opt->flag==0&& opt->val>='A'&& opt->val<='z'){*osp++= opt->val;switch(opt->has_arg){case optional_argument:*osp++=':';*osp++=':';break;case required_argument:*osp++=':';break;default:break;}}}return optstring;}intmain(int argc,char**argv){staticstructoption long_options[]={{"smile",no_argument,NULL,'s'},{"cry",no_argument,NULL,'c'},{"tired",no_argument,NULL,'t'},{"nums",required_argument,NULL,'n'},{"rand",optional_argument,NULL,'r'},{NULL,0,0,0}};constchar*optstring=optstring_from_long_options(long_options);structstate st;int i=0, n=0;while(1){int option_index=0;int arg=getopt_long(argc,argv,optstring,long_options,&option_index);if(arg==-1)break;switch(arg){case0:fprintf(stderr,"Unknow option :%s", long_options[option_index].name);exit(-1);break;case's':
st.smile_nums=1;break;case'c':
st.cry_nums=1;break;case't':
st.tired_nums=1;break;case'n':
st.nums=atoi(optarg);break;case'r':// 随机生成optarg个相同的表情if(optarg)
n=atoi(optarg);else
n=1;srand(time(NULL));
i=rand()%st.kinds;if(i==0)
st.smile_nums= n;elseif(i==1)
st.cry_nums= n;else
st.tired_nums= n;break;default:fprintf(stderr,"Unknow option :%s", long_options[option_index].name);exit(-1);break;}}if(st.nums!=0){if(st.smile_nums) st.smile_nums= st.nums;if(st.cry_nums) st.cry_nums= st.nums;if(st.tired_nums) st.tired_nums= st.nums;}// 根据读取的参数,绘制表情for(int i=0; i<st.smile_nums; i++)print_logo(smile_str);for(int i=0; i<st.cry_nums; i++)print_logo(cry_str);for(int i=0; i<st.tired_nums; i++)print_logo(tired_nums);return0;}
执行结果如下:
./long_options --tired
_ _ _||(_)||||_ _ _ __ ___ __||| __||'__/ _ \/ _`|||_|||| __/(_||\__|_|_|\___|\__,_|
./long_options -c -n2
/ __|'__| | | |
| (__| | | |_| |
\___|_| \__, |
__/ |
|___/
/ __| '__|||||(__||||_||\___|_|\__,|
__/||___/
./long_options -r2
_ _(_)|
___ _ __ ___ _|| ___
/ __| '_` _ \| | |/ _ \
\__ \ | | | | | | | __/
|___/_| |_| |_|_|_|\___|
_ _
(_) |
___ _ __ ___ _| | ___
/ __| '_` _\|||/ _\\__\||||||| __/|___/_||_||_|_|_|\___|
./long_options -st
_ _(_)|
___ _ __ ___ _|| ___
/ __| '_` _\|||/ _\\__\||||||| __/|___/_||_||_|_|_|\___|
_ _ _||(_)||||_ _ _ __ ___ __||| __|| '__/ _\/ _`|||_|||| __/(_||\__|_|_|\___|\__,_|
相关链接
附录
其他代码
头文件:comm.h
#ifndef COMM_H
#define COMM_H
#include <vector>
#include <string>
#include <iostream>
// logo相关的变量声明和函数
extern const std::vector<std::string> smile_str;
extern const std::vector<std::string> cry_str;
extern const std::vector<std::string> tired_nums;
void print_logo(const std::vector<std::string> &logo);
#endif
logo相关函数和数据结构。
#include <vector>
#include <string>
#include <iostream>
#include "comm.h"
const std::vector<std::string> smile_str = {
" _ _ ",
" (_) | ",
" ___ _ __ ___ _| | ___ ",
" / __| '_ ` _ \\| | |/ _ \\",
" \\__ \\ | | | | | | | __/",
" |___/_| |_| |_|_|_|\\___|",
" ",
" "
};
const std::vector<std::string> cry_str = {
" ",
" / __| '__| | | |",
" | (__| | | |_| |",
" \\___|_| \\__, |",
" __/ |",
" |___/ ",
" "
};
const std::vector<std::string> tired_nums = {
" _ _ _ ",
" | | (_) | |",
" | |_ _ _ __ ___ __| |",
" | __| | '__/ _ \\/ _` |",
" | |_| | | | __/ (_| |",
" \\__|_|_| \\___|\\__,_|",
" ",
" "
};
void print_logo(const std::vector<std::string> &logo){
int n = logo.size();
for(int i=0; i<n; i++)
std::cout<<logo[i]<<std::endl;
}
编译过程:makefile
all:long_options
long_options:long_options.cpp logo.cpp
g++ -o $@ $^ -I .
clean:
rm -f long_options
非选项参数处理
下面的SOURCE
和DEST
为非选项参数。本篇没有介绍,非选项参数的处理。
cp[OPTION]...[-T] SOURCE DEST
使用optind
变量很容易处理。参见示例:打印参数