“命令终端”的实现4-优化之解耦
这段时间一直在做测试的工程(不是测试的工作),为了应付不同的测试场景,代码使用了解释器风格,至于实现,则使用了多年前写的命令终端代码。那会刚毕业不久,写的代码还是有提升空间。现在重新拾起,打破一般认知中的看不懂6个月前写的代码的刻板印象。
存在问题
原来的工程使用C代码编写,并不严格区分测试代码和实现代码,其中最大的问题是将命令列表做成全局变量并依赖于外部的定义,这样耦合程序非常高。因此需要分离出来。
原工程的文件命名也不太好,如common.h
这样的文件,在与其它工程整合时容易冲突,此次一并修改了。
解耦
依赖定义全局的命令列表,但只是指针,添加注册命令接口,由外部使用者调用。将默认的帮助命令调整至内部实现,外部直接使用。代码如下:
static cmd_tbl_t* cmd_table;
static int cmd_table_len = 0;/*register command
*/
void register_command(cmd_tbl_t* table, int len)
{cmd_table = table;cmd_table_len = len;
}int do_help_default(int argc, char* argv[])
{_do_help_default(cmd_table, argc, argv);return 0;
}
在原有测试代码基础上,添加初始化命令函数,如下:
/* 定义命令列表 */
cmd_tbl_t my_cmd_table[] =
{// do_help_default为默认函数,可重新实现{"help", CONFIG_SYS_MAXARGS, do_help_default, "print help info."},{"print", 2, do_print, "print the env."},{"exit", 1, do_exit, "exit..."},{"quit", 1, do_exit, "exit..."},
};// 初始化,注册命令
void cmd_init()
{int len = sizeof(my_cmd_table) / sizeof(my_cmd_table[0]);register_command(my_cmd_table, len);
}
这样,在主体函数开始处调用cmd_init();
即可。如此一来,结构清晰,逻辑也清晰。
历史命令优化
命令终端支持历史命令,由HIST_MAX
决定数量。默认为 10 个。历史命令使用hist_list
存储,添加命令函数如下:
static void cread_add_to_hist(char *line)
{strcpy(hist_list[hist_add_idx], line);if (++hist_add_idx >= HIST_MAX)hist_add_idx = 0;if (hist_add_idx > hist_max)hist_max = hist_add_idx;hist_num++;
}
实现很简单,先添加(因为索引从0开始),再累加并与最大值比较。达到最大值后,替换存储的第0个命令。在实际执行中,有时会出现上下命令相同的情况,此时无须再次保存,以节省空间。
修改后代码:
static void cread_add_to_hist(char *line)
{// 判断是否为相同的命令(第0条命令没有相同的说法)if (hist_add_idx > 1 && !strcmp(hist_list[hist_add_idx-1], line)) return;strcpy(hist_list[hist_add_idx], line);if (++hist_add_idx >= HIST_MAX)hist_add_idx = 0;if (hist_add_idx > hist_max)hist_max = hist_add_idx;hist_num++;
}
非命令行模式
截至目前,“命令终端”只有命令行模式,即执行程序初始化后,只会进入命令提示符界面,等待用户输入命令,再解析、执行。有时候,某些场景需要直接执行命令,即自动执行用户输入的命令,亦即将用户命令作为程序的参数。比如,将:
NotAShell> print abc
NotAShell> print 100 200
改为
./a_all.out "print abc; print 100 200;"
的形式,直接执行一次程序即可得到结果,不用手工输入命令。
实现源码:
// 去掉前后空格——中间的不去掉
std::string& trim(std::string &str)
{if (str.empty()){return str;}str.erase(0, str.find_first_not_of(" ")); //去除左边空格str.erase(str.find_last_not_of(" ") + 1);//去除右边空格return str;
}// 分割string,能自动去掉分隔符前后的空格
std::vector <std::string> splitString(const std::string & s, const std::string & delim)
{std::vector <std::string> elems;std::string tmp;size_t pos = 0;size_t len = s.length();size_t delim_len = delim.length();if (delim_len == 0)return elems;while (pos < len){int find_pos = s.find(delim, pos);if (find_pos < 0){tmp = s.substr(pos, len - pos);elems.push_back(trim(tmp));break;}tmp = s.substr(pos, find_pos - pos);elems.push_back(trim(tmp));pos = find_pos + delim_len;}return elems;
}//*
本函数功能:
用于测试组装命令的场景。
使用如下:
./a_all.out "print abc; print 100 200;"
即只有一个参数。为了复用已有命令终端,将参数分析还原为argc argv形式,再调用
*/int readline_cmd_allone(int argc, char ** argv)
{if (argc < 2) return -1;// 命令参数最大为10个
#define MAX_ARGC 10std::vector<std::string> v = splitString(argv[1], ";");cmd_tbl_t *ptable = NULL;char* myargv[MAX_ARGC] = {NULL};int myargc = 0;for(unsigned int i=0; i<v.size(); i++){// printf("split: [%s] %d\n", v[i].c_str(), v[i].empty());if(v[i].empty()){continue;}char cmd[128];memcpy(cmd, v[i].c_str(), 128);// 是否再转成argv的形式??std::vector<std::string> vv = splitString(cmd, " ");myargc = (int)vv.size();myargc = myargc > MAX_ARGC ? MAX_ARGC : myargc;for (int j = 0; j < myargc; j++){myargv[j] = (char*)vv[j].c_str();}// for (int j = 0; j < myargc; j++)// {// printf("myargv[%d]: %s\n", j, myargv[j]);// }ptable = find_table(myargv[0]);if (ptable == NULL){printf("cmd name: [%s] not found\n", myargv[0]);continue;}printf("name: %s\n", ptable->name);ptable->cmd(myargc, myargv);}return 0;
}
主函数变更如下:
int main(int argc, char* argv[])
{char* p;char* cmdname = *argv;if ((p = strrchr (cmdname, '/')) != NULL){cmdname = p + 1;}if (strcmp(cmdname, "a.out") == 0){if (readline_cmd(argc, argv) != 0)return -1;return 0;}else if (strcmp(cmdname, "a_all.out") == 0){if (readline_cmd_allone(argc, argv) != 0)return -1;return 0;}return 0;
}
代码以a.out
和a_all.out
为执行文件名称作为示例。不管哪种形式,都可以直接复用已有的模块。笔者实际使用的场景,是一个用于自测的程序,有时,需要手动修改参数进行测试,有时需要将程序放到后台执行(因为耗时较长)。
“命令终端”的实现4-优化之解耦相关推荐
- Debian11镜像更新为阿里巴巴开源镜像站镜像,切换root用户,解决用户名不在sudoers文件中此事将被报告,Debian11 文件夹对话框、火狐浏览器、命令终端等没有最大化和最小化
选择Debian作为编程开发最佳Linux的理由: Debian是面向程序员的最古老,最出色的Linux发行版之一.Debian提供了具有.deb软件包管理兼容性的超稳定发行版.Debian为程序员提 ...
- Golang实践录:命令行cobra库实例优化
本文上一文章<Golang实践录:命令行cobra库实例> 的优化,主要的子命令的业务实现的整理. 起因 旧版本中,每个子命令的入口函数,均需一一判断传入参数,并调用对应的业务实现函数,编 ...
- Ubuntu命令终端查看使用过的命令
使用history命令 cyf@ubuntu:~$ history 但是这样会显示出所有使用过的命令,可以在history后加上less cyf@ubuntu:~$ history | less 会显 ...
- Win7命令终端基础配色指南
微软对控制台字体的元数据有严格的限制,https://support.microsoft.com/zh-cn/help/247815/necessary-criteria-for-fonts-to-b ...
- Windows 如何在命令终端(CMD)使用命令来访问本地/远程的 Oracle 数据库呢?
打开命令窗口后直接输入 sqlplus sys/123@orcl as sysdba 其中 sys 是用户名,123 是密码,orcl 是数据库实例名,as sysdba 表示用户 sys 是数据库管 ...
- Windows 如何用命令终端(CMD)启动和停止 MySQL 数据库服务
当安装完 MySql 后,默认每次 Windows 启动的时候都会将 MySql 服务启动起来.那么如何通过命令方式来启动和停止 MySQL 服务呢? Windows XP 如果是 Windows X ...
- Linux 的命令终端(CMD)的快捷键(Keyboard of MacBook)
文章目录 常用 移动光标 编辑命令 查找历史命令 控制命令 命令终端界面滚屏 命令终端页签切换 奇葩 常用 快捷键 说明 Ctrl + A 光标跳到本行的行首 Ctrl + E 光标跳到本行的行尾 C ...
- Windows 命令终端(CMD)的快捷键
这些快捷键只在Windows系统操作有效,连接远程Linux主机,再操作这些快捷键是无效的,因为连接远程的Linux主机后,你用的是Linux命令终端. 快捷键: F1:按F1逐字显示最后一次执行的命 ...
- 如何通过命令终端访问本地/局域网/远程的MySQL数据库_访问数据库_连接数据库_登录数据库
文章目录 Windows系统下 访问本地MySQL数据库 访问远程主机的MySQL数据库 本地安装了MySQL数据库 本地没有安装MySQL Linux系统下 退出数据库登录 Windows系统下 访 ...
最新文章
- 中国电子学会青少年编程能力等级测试图形化三级编程题:海底寻宝
- 添加javascript代码:_JavaScript的使用
- 什么是mysql分发版_MySQL:使用源码分发版还是二进制分发版
- 大话PM|产品设计中常被忽视的业务异常
- python爬虫与django_请问django和爬虫程序如何整合?
- 沈伟华:图神经网络的三连问
- centos7 安装nginx报错./configure: error: the HTTP rewrite module requires the PCRE library
- Qt linux双屏,qt5 多屏显示
- python2.7初学(〇)
- node.js(二)创建服务器
- Android Contentprovider的学习实例总结
- 一个简单的makefile编写(gcc)
- 如何恢复删除的文件?4种常用方法教你恢复被删除的文件
- 谈谈自我介绍与第一印象
- dw网页制作的基本步骤_网页制作一般使用哪些工具?DW使用方法教学?
- hexo(sakura)给博客增添侧边栏(回到顶部,跳转评论,深色模式,播放音乐)Valine-1.4.4新版本尝鲜+个性制定(表情包、qq头像、UI样式)
- 软件测试|测试金字塔是什么,它的目的是什么,以及它包含哪些层次?
- 字体颜色 * 博客 * 好看
- html实现放大镜效果,利用jquery实现放大镜特效
- 简述直方图和柱形图的区别_什么是直方图?跟柱状图有什么区别?终于有人讲明白了...
热门文章
- python常用内置方法_Python3 常用的几个内置方法
- JavaScript鼠标经过图片加亮显示
- 在周二正式发售前 新款MacBook Pro已开始向客户发货
- 华为Mate 50系列明年亮相:或首发高通骁龙898 4G芯片
- Apple Watch移动心电图ECG与房颤提示功能获药监局审核
- 已完成私有化交易 “网红第一股”如涵退市
- 年初至今主动权益基金平均亏损2.96%
- 蚂蚁集团前三季度营收1181.91亿元 支付宝月活用户7.31亿
- iPhone 12无线充电模块曝光:AirPower有戏了!
- iPhone 12系列屏幕细节曝光:三星OLED屏独霸大半