鹤壁网站建设,网站域名后缀有什么用,平台直播,短视频代运营命令行解析库是一种用于简化处理命令行参数的工具#xff0c;它可以帮助开发者更方便地解析命令行参数并提供适当的帮助信息。C语言中#xff0c;常用的命令行解析库有许多#xff0c;通过本文的学习#xff0c;读者可以了解不同的命令行解析库和它们在C项目中的应用#…命令行解析库是一种用于简化处理命令行参数的工具它可以帮助开发者更方便地解析命令行参数并提供适当的帮助信息。C语言中常用的命令行解析库有许多通过本文的学习读者可以了解不同的命令行解析库和它们在C项目中的应用从而更加灵活和高效地处理命令行参数。
一般比较常见的解析库
getopt一个C语言的命令行解析库也被广泛用于C程序。它提供了一组函数来解析命令行参数并支持短选项如 -a和长选项如 --help。Boost.Program_options这是Boost库中的一个模块提供了一个强大的命令行解析库。它支持短选项、长选项、位置参数等并且具有很好的错误处理和帮助信息生成功能。TCLAPTCLAPTemplatized C Command Line Parser Library是一个C的命令行解析库它提供了简单易用的API来解析命令行参数并支持短选项和长选项。CLI11CLI11是一个现代化的C命令行解析库它使用C11标准并提供了一组简单易用的API。
这些库各有特点开发者可以根据项目的需求和个人喜好选择合适的命令行解析库。通过使用这些库开发者可以更轻松地处理命令行参数提高程序的易用性和用户体验。在命令行程序中argc和argv是C程序中用于接收命令行参数的主要机制。其中
argcArgument Count表示命令行参数的个数包括程序本身。至少为1即程序名。argvArgument Vector是一个指向字符指针数组的指针每个指针指向一个以空字符结尾的C字符串表示一个命令行参数。
通常argv[0] 存储的是程序的名称argv[1] 开始存储的是传递给程序的实际命令行参数。开发者可以通过对 argc 和 argv 的处理来获取和解析命令行参数从而完成特定功能的配置和操作。例如使用 getopt、Boost.Program_options 或者其他命令行解析库来解析和处理命令行参数更方便地获取用户的输入。
9.1 在字符串上解析
该段代码通过简单的字符串切割实现了对命令行参数的解析。它将命令行字符串切割为选项和参数并输出它们的内容。同时根据特定的选项和参数组合条件输出用户登录的相关信息。
代码的主要流程如下
定义了一个函数 GetOpt该函数接收一个字符指针 command命令行字符串和一个二维字符数组 command_ptr用于存储解析后的选项和参数。在 GetOpt 函数中使用 strtok 函数对 command 字符串进行切割和解析将选项和参数存储在 command_ptr 数组中并返回选项和参数的总数。在 main() 函数中构造一个命令行字符串 cmd然后调用 GetOpt 函数将解析后的选项和参数存储在 Opts 数组中并获取选项和参数的总数 count。使用 for 循环遍历 Opts 数组根据数组索引的奇偶性分别输出命令行选项和参数。
读者需要注意此代码使用简单的字符串切割来实现命令行参数的解析并假设输入的命令行格式是固定的即选项和参数的顺序和格式是固定的如 “–address 127.0.0.1 --password 123456 --port 22”。如果输入的命令行格式有变化或者更复杂的需求可能需要使用更强大的命令行解析库来完成更灵活的解析工作。
#include iostream
#include Windows.husing namespace std;// 传入命令行,切割解析
int GetOpt(IN char *command, OUT char command_ptr[][1024])
{char* ptr;ptr strtok(command, );int count 0;while (ptr ! NULL){strcpy(command_ptr[count], ptr);ptr strtok(NULL, );}return count;
}int main(int argc, char* argv[])
{char cmd[4096] --address 127.0.0.1 --password 123456 --port 22;char Opts[30][1024];int count GetOpt(cmd, Opts);for (int x 0; x count; x){if (x % 2 0){std::cout 命令行: Opts[x] std::endl;}else{std::cout 参数: Opts[x] std::endl;}}// 参数解析使用if ((strcmp(Opts[0], --address) 0) (strcmp(Opts[2], --password) 0) (strcmp(Opts[4], --port) 0)){std::cout 用户登录: Opts[1] 密码: Opts[3] 端口: Opts[5] std::endl;}return 0;
}9.2 自实现参数解析
这段代码是笔者突发奇想之后写出来的一个简易版参数解析器通过检查参数个数和特定的选项和参数组合输出对应的类型、地址和端口信息。如果参数个数小于等于2则输出使用说明如果参数个数等于7且满足特定格式 “–type tcp/udp --address 127.0.0.1 --port 8888”则输出用户指定的类型、地址和端口信息。
代码的主要流程如下
通过检查 argc 的值如果小于等于2则输出使用说明提示用户正确输入命令行参数。如果参数个数等于7按照特定的格式 “–type tcp/udp --address 127.0.0.1 --port 8888” 进行解析和判断。使用 strcmp 函数判断命令行选项是否为 “–type”、“–address” 和 “–port”并检查其后的参数是否符合预期格式。
根据特定的选项和参数组合条件输出对应的类型、地址和端口信息。
#include Windows.h
#include iostreamint main(int argc, char* argv[])
{// 如果小于两个参数则输出提示if (argc 2){fprintf(stderr, \nUsage:\n\n \t --type 指定类型(string) \n \t --address 指定地址(string) \n \t --port 指定端口(int) \n\n);exit(0);}// 如果参数个数是7那么总共需要有6个参数传递// 其中 1,3,5 代表的是参数开关// 剩余 2,4,6 则代表每个开关传入参数if (argc 7){// --type tcp/udp --address 127.0.0.1 --port 8888if (strcmp((char*)argv[1], --type) 0 strcmp((char*)argv[3], --address) 0 strcmp((char*)argv[5], --port) 0 ){// 开关内部,也可以嵌套继续判断类型if (strcmp((char*)argv[2], tcp) 0){printf([] 类型: %s 地址: %s 端口: %d \n, argv[2], argv[4], atoi(argv[6]));}else if (strcmp((char*)argv[2], udp) 0){printf([] 类型: %s 地址: %s 端口: %d \n, argv[2], argv[4], atoi(argv[6]));}}}return 0;
}如上代码所示是笔者最常用的命令行解析方式这种方式比较死板无法更智能的判断参数类型如果需要判断的更全面则需要将其改进为以下格式改进后虽然解析更灵活了但管理起来也会变得更复杂。
如下所示代码实现了一个32位端口快速扫描器的简单功能。通过解析命令行参数用户可以指定待扫描的IP地址、开始端口和结束端口并根据参数选择相应的扫描方式。如果没有指定合法的参数或缺少必要参数则输出工具的菜单选项供用户参考。
代码的主要流程如下
定义了一个结构体 GetOpt 用于保存参数信息并定义了全局变量 opt 作为全局参数存储对象。编写函数 getOpts 对命令行参数进行解析并将解析结果存储到结构体 opt 中。编写函数 ShowOptions 输出工具的菜单选项包含待扫描的IP地址、开始端口和结束端口的参数说明。
在 main() 函数中根据命令行参数的解析结果输出对应的信息如果同时指定了IP地址、开始端口和结束端口则输出对应的扫描信息如果只指定了开始端口和结束端口则输出端口范围信息否则显示工具的菜单选项。
#define _CRT_SECURE_NO_WARNINGS
#include stdio.h
#include Windows.htypedef struct GetOpt // 全局保存每个参数
{char Address[128]; // IP地址int Start; // 开始端口int End; // 结束端口
}GetOpt;static struct GetOpt opt; // 定义全局结构体// getOpts 针对参数的解析与赋值
int getOpts(int argc, char **argv)
{strcpy(opt.Address, null); // 初始化参数解析opt.Start 0; opt.End 0; // 初始化参数解析for (int each 1; each argc; each){if (!strcmp(argv[each], --addr) each 1 argc){strcpy(opt.Address, argv[each]);}else if (!strcmp(argv[each], --start) each 1 argc){opt.Start atoi(argv[each]);}else if (!strcmp(argv[each], --end) each 1 argc){opt.End atoi(argv[each]);}else { return 0; }}return 1;
}// 输出工具菜单选项
void ShowOptions()
{fprintf(stderr, \nUsage: 32位端口快速扫描器 Ver:1.0 By:Lyshark \n\noptions: \n\t --addr [addr] 指定待扫描的Ip地址 \n\t --start [count] 指定待扫描的开始端口 \n\t --end [count] 指定待扫描的结束端口 \n);
}// 主函数还是用来判断参数,并执行相应的命令
int main(int argc, char* argv[])
{if (getOpts(argc, argv) ! 1){ShowOptions();}else if (strcmp(opt.Address, null) ! 0 opt.Start ! 0 opt.End ! 0){for (int x 0; x 100; x)printf(扫描: %s 开始地址: %d 结束地址: %d \n, opt.Address, x, opt.End);}else if (opt.Start ! 0 opt.End ! 0){for (int y 0; y 10; y)printf(端口范围: %d - %d \n, y, opt.End);}else { ShowOptions(); }return 0;
}9.3 交互式参数解析
交互式参数解析器其实就是类似于Linux系统终端那样的页面运行代码后进入一个可交互环境读者可以执行需要的命令。该功能的实现依赖于tokenizer.hpp模块该模块提供了灵活、高效的字符串分割工具可以帮助简化字符串处理的任务特别是在文本处理、配置文件解析、数据解析等方面有着广泛的应用。
boost/tokenizer.hpp 主要功能是将一个字符串拆分成多个子串tokens通过指定分隔符或者符合某种条件的位置来实现字符串的分割。这在处理文本文件、解析命令行参数、数据处理等方面非常有用。
使用 boost::tokenizer 需要包含 boost/tokenizer.hpp 头文件并在代码中使用 boost::tokenizer 类的实例对象来进行字符串的分割。该类提供了灵活的选项允许用户指定分隔符、忽略空白字符、指定分隔符类型等。
例如下面是一个使用 boost::tokenizer 进行字符串分割的简单示例
#include iostream
#include string
#include boost/tokenizer.hppint main()
{std::string input Boost C Libraries;boost::tokenizer tokens(input); // 默认使用空格作为分隔符for (const auto token : tokens) {std::cout token std::endl;}return 0;
}上述示例会将字符串 Boost C Libraries 按照空格进行分割并输出拆分得到的子串。运用此功能并配合一个死循环结构我们就可以构建出一个交互式命令行环境并可以根据用户输入的命令执行相应的操作。
根据上述所示的库函数我们可以灵活的实现参数的解析功能并实现一个简单的交互式参数解析功能如下所示将提供三个交互命令读者可自行编译并运行测试。
代码的主要流程如下
使用 std::getline(std::cin, command) 从标准输入读取用户输入的命令并将命令存储在字符串 command 中。通过字符串的比较判断用户输入的命令如果是 “help” 则输出功能菜单展示可用的命令选项。使用 boost::tokenizer 将用户输入的命令进行分割提取出命令关键词和参数。根据分割后的命令关键词和参数执行相应的功能 如果是 “AddRule” 命令则解析地址和DNS参数并输出解析结果。如果是 “DeleteRule” 命令则解析地址参数并输出删除地址信息。如果是 “ShowList” 命令则输出一个简单的数字列表。
#include iostream
#include string
#include boost/tokenizer.hppusing namespace std;
using namespace boost;int main(int argc, char const *argv[])
{std::string command;while (1){std::cout [LyShark] # ;std::getline(std::cin,command);if (command.length() 0){continue;}else if (command help){std::cout [功能菜单] \n std::endl;std::cout 增加规则: AddRule --address 192.168.1.1 --dns 8.8.8.8 std::endl;std::cout 删除规则: DeleteRule --address 192.168.1.1 std::endl;std::cout 输出列表: ShowList std::endl;}else{// 定义分词器: 定义分割符号为[逗号,空格]boost::char_separatorchar sep(, --);typedef boost::tokenizerboost::char_separatorchar CustonTokenizer;CustonTokenizer tok(command, sep);// 将分词结果放入vector链表std::vectorstd::string vecSegTag;for (CustonTokenizer::iterator beg tok.begin(); beg ! tok.end(); beg){vecSegTag.push_back(*beg);}// 解析 [shell] # AddRule --address 192.168.1.1 --dns 8.8.8.8if (vecSegTag.size() 5 vecSegTag[0] AddRule){if (vecSegTag[1] address vecSegTag[3] dns){std::string set_address vecSegTag[2];std::string set_dns vecSegTag[4];std::cout 解析地址: vecSegTag[2] 解析DNS: vecSegTag[4] std::endl;}}// 解析 [shell] # DeleteRule --address 192.168.1.1else if (vecSegTag.size() 3 vecSegTag[0] DeleteRule){if (vecSegTag[1] address){std::string del_address vecSegTag[2];std::cout 删除地址: del_address std::endl;}}// 解析 [shell] # ShowListelse if (vecSegTag.size() 1 vecSegTag[0] ShowList){for (int x 0; x 10; x){std::cout x std::endl;}}}}return 0;
}9.4 非交互参数解析
虽然分词器可以用于参数解析但是其本身并不是用于做参数解析用的在Boost中提供了Boost.Program_options库该框架提供了强大而灵活的命令行选项解析功能可以帮助简化处理命令行参数的过程并提供良好的帮助信息和错误处理机制是处理命令行参数的优秀工具库之一。
使用 Boost.Program_options 需要包含 boost/program_options.hpp 头文件并通过创建 boost::program_options::options_description 对象来定义选项描述然后使用 boost::program_options::parse_command_line 函数解析命令行参数最后通过 boost::program_options::variables_map 对象获取解析后的选项和参数的值。
例如下面是一个使用 Boost.Program_options 解析命令行参数的简单示例
代码的主要流程如下
使用 boost::program_options::options_description 定义命令行选项描述包含三个选项address、start_port 和 end_port以及一个 help 选项用于输出帮助菜单。使用 boost::program_options::parse_command_line 函数解析命令行参数并将解析结果存储在 boost::program_options::variables_map 对象 virtual_map 中。使用 boost::program_options::notify 函数检查命令行参数是否符合预期并存储解析后的值到 virtual_map。根据 virtual_map中存储的命令行参数值判断用户输入的选项并执行相应的操作 如果用户输入了 --help 或 -h 选项则输出帮助菜单。如果用户输入了 --address、--start_port 和 --end_port 选项则输出扫描地址、开始端口和结束端口的信息。如果用户输入了未定义的选项或缺少必需的选项则输出参数错误信息。
通过使用 Boost.Program_options 库可以更方便地定义和解析命令行选项从而使程序的命令行使用更加友好和灵活。
#include iostream
#include boost/program_options.hppnamespace opt boost::program_options;int main(int argc, char const *argv[])
{opt::options_description des_cmd(\n Usage: 32位端口快速扫描器 Ver:1.0 \n\n Options);des_cmd.add_options()(address,a, opt::valuestd::string()-default_value(127.0.0.1), 指定扫描地址)(start_port,s, opt::valueint()-default_value(0), 扫描开始端口)(end_port,e, opt::valueint()-default_value(65535), 扫描结束端口)(help,h, 帮助菜单);opt::variables_map virtual_map;try{opt::store(opt::parse_command_line(argc, argv, des_cmd), virtual_map);}catch (...){ return 0; }// 定义消息opt::notify(virtual_map);// 无参数直接返回if (virtual_map.empty()){return 0;}else if (virtual_map.count(help) || virtual_map.count(h)){std::cout des_cmd std::endl;return 0;}else if (virtual_map.count(address) virtual_map.count(start_port) virtual_map.count(end_port)){std::string address virtual_map[address].asstd::string();int start_port virtual_map[start_port].asint();int end_port virtual_map[end_port].asint();// 判断是不是默认参数if ( address 127.0.0.1 || start_port 0 || end_port 65535){std::cout des_cmd std::endl;}else{std::cout 开始扫描: address 开始地址: start_port 结束地址: end_port std::endl;}}else{std::cout 参数错误 std::endl;}return 0;
}当然了上述代码中我们也可以单独增加一个Banner()函数并将其放入到virtual_map.empty()无参数模式这样一来当参数输入不当或无参数是则会打印输出我们自己的点阵标志能使程序变得更友好。
#include iostream
#include boost/program_options.hppnamespace opt boost::program_options;void Banner()
{printf( _ _ _ \n);printf(| |_ _ ___| |__ __ _ _ __| | __ \n);printf(| | | | / __| _ \\ / _ | __| |/ / \n);printf(| | |_| \\__ \\ | | | (_| | | | \n);printf(|_|\\__, |___/_| |_|\\__,_|_| |_|\\_\\ \n);printf( |___/ \n\n);
}int main(int argc, char const *argv[])
{opt::options_description des_cmd(\n Usage: 输出Logo Ver:1.0 \n\n Options);des_cmd.add_options()(address,a, opt::valuestd::string(), 指定扫描地址)(start_port,s, opt::valueint(), 扫描开始端口)(end_port,e, opt::valueint(), 扫描结束端口)(help,h, 帮助菜单);opt::variables_map virtual_map;try{opt::store(opt::parse_command_line(argc, argv, des_cmd), virtual_map);}catch (...){ return 0; }// 定义消息opt::notify(virtual_map);// 无参数直接返回if (virtual_map.empty()){Banner();std::cout des_cmd std::endl;return 0;}// 帮助菜单else if (virtual_map.count(help) || virtual_map.count(h)){Banner();std::cout des_cmd std::endl;return 0;}// 分支结构1else if (virtual_map.count(address) virtual_map.count(start_port) virtual_map.count(end_port)){std::string address virtual_map[address].asstd::string();int start_port virtual_map[start_port].asint();int end_port virtual_map[end_port].asint();std::cout 开始扫描: address 开始地址: start_port 结束地址: end_port std::endl;}// 分支结构2else if (virtual_map.count(address)){std::string address virtual_map[address].asstd::string();std::cout 地址: address std::endl;}else{std::cout 参数错误 std::endl;}return 0;
}本文作者 王瑞 本文链接 https://www.lyshark.com/post/8b095ec4.html 版权声明 本博客所有文章除特别声明外均采用 BY-NC-SA 许可协议。转载请注明出处