介绍CppShell
写在前面
最近心血来潮又造了个轮子,其实启发我的是bajdcc/jMiniLang中的管道思想,java运行着太慢,因而用C艹实现一把。
如题图所示,使用非常非常简单。
- range生成有限/无穷数列,`range 0`生成自然数无穷数列,`range 1 10`生成1到10
- take N,表示从有限/无穷数列中摘取前N行
- last N,表示从有限数列中取倒数N行,当然了如果数列是无穷的,那么GG
- load FILENAME,加载文件
- save FILENAME,保存至文件
设计思路
首先当然是解析命令行输入啦,然后则是处理与输出。
一、处理命令
假如命令是诸如`load 1.txt | uppercase | save 2.txt`,以“|”作分隔,分隔后得到单一程序命令行,再以空格作分隔。
用式子来表示是:
- 用户输入command_string
- applications = command_string.split('|')
对applications中每一app,app_args = app.split(' '),然后app_name = app_args[0],删除app_args[0],得到后面的参数arguments下面的任务是,根据app_name和arguments来创建应用程序。
二、创建应用程序
我们以`range 1 100 | save 2.txt`为例,意义为“生成1到100的数列,然后保存至文件”。
所以必须生成两个程序range和save,那么两者是什么关系呢?
思考一下:生成数列,将数列保存至文件。即:以数列作输入,文件为输出。得到:range => save。即save的输入是range的输出,是从前向后逐渐依赖的关系,换句话说,后者调用前者。
这里用到了装饰者模式,既然后者调用前者,那么后者将前者包裹起来即可。应用程序的创建涉及工厂模式,没什么大过花哨的C++技巧。
三、应用程序接口设计
其实也就是“流”接口的设计。参考众多流设计,这里我只实现最简单的:
- bool available() const,返回流是否可用/到末尾
- char next(),读取当前字符,并准备下个字符
应用程序只要重载这两个接口即可。
代码实现
1. Shell
void CShell::exec(const std::string& cmd)
{
auto s = std::split(cmd, '|');
std::vector<app_t> cmder;
std::vector<std::string> names;
std::vector<std::vector<std::string>> arg;
for (auto& str : s)
{
str = std::trim(str);
auto part = std::split(str, ' ');
if (part.empty())
return error("empty argument");
names.push_back(part[0]);
auto apt = CApp::get_type_by_name(part[0]);
if (apt == app_none)
return error("invalid application: " + str);
part.erase(part.begin());
cmder.push_back(apt);
arg.push_back(part); // 应用程序参数
}
auto inner = CApp::create(app_null); // 最里层程序
std::shared_ptr<CApp> app;
for (uint32_t i = 0; i < cmder.size(); i++)
{
app = CApp::create(cmder[i]); // 工厂模式创建应用程序
if (app->set_arg(arg[i]) != 0)
return error(names[i] + ": " + app->get_err());
app->set_inner_app(inner); // 装饰模式进行包装
inner = app;
}
while (app->available()) // 正式工作!
{
auto c = app->next();
if (c != '\0')
std::cout << c;
}
} void CShell::error(const std::string& str)
{
std::cerr << str << std::endl;
}
2. App
enum app_t
{
app__begin,
app_none,
app_null,
app_pipe,
app_range,
app_take,
app_last,
app_load,
app_save,
app__end
}; class CApp
{
public:
CApp();
virtual ~CApp(); static std::shared_ptr<CApp> create(app_t type);
static app_t get_type_by_name(const std::string &name); int set_arg(std::vector<std::string> arg);
virtual int init() = 0; void set_inner_app(std::shared_ptr<CApp> app); std::string get_err() const; virtual bool available() const = 0;
virtual char next() = 0; protected:
std::vector<std::string> args;
std::string error;
std::shared_ptr<CApp> inner;
}; // 创建
std::shared_ptr<CApp> CApp::create(app_t type)
{
switch (type)
{
case app_none:
break;
case app_null:
return std::make_shared<CAppNull>();
case app_pipe:
return std::make_shared<CAppPipe>();
case app_range:
return std::make_shared<CAppRange>();
case app_take:
return std::make_shared<CAppTake>();
case app_last:
return std::make_shared<CAppLast>();
case app_load:
return std::make_shared<CAppLoad>();
case app_save:
return std::make_shared<CAppSave>();
default:
break;
}
assert(!"invalid type");
return nullptr;
}
3. AppLoad
就举这一个例子吧
int CAppTake::init() // 初始化
{
if (args.size() == 1) // 有一个参数
{
start = 1; // 计数开始
end = atoi(args[0].c_str()); // 计数结束
}
else
{
error = "invalid argument size";
return -1;
}
return 0;
} bool CAppTake::available() const
{
return start <= end || !data.empty();
} char CAppTake::next()
{
if (data.empty())
{
if (!available()) // 上一流已经中止
return '\0';
while (inner->available()) // 上一流有数据
{
auto c = inner->next();
data.push(c);
if (c == '\n') // 读取一行到data中
break;
}
start++; // 计数加一
if (data.empty()) // 没有数据了
return '\0';
}
auto ch = data.front(); // 输出读取的一行数据
data.pop();
return ch;
}
阶段性总结
总之,做这个轮子还是挺愉悦的~因为并未脱离舒适区。。就当复习吧。
好吧,其实写这玩意是因为bash中的awk、sed、grep等查找替换太复杂了,还不如自己做个。
由https://zhuanlan.zhihu.com/p/26591115备份。
介绍CppShell的更多相关文章
- CSS3 background-image背景图片相关介绍
这里将会介绍如何通过background-image设置背景图片,以及背景图片的平铺.拉伸.偏移.设置大小等操作. 1. 背景图片样式分类 CSS中设置元素背景图片及其背景图片样式的属性主要以下几个: ...
- MySQL高级知识- MySQL的架构介绍
[TOC] 1.MySQL 简介 概述 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司. MySQL是一种关联数据库管理系统,将数据保存在不同的表中,而 ...
- Windows Server 2012 NIC Teaming介绍及注意事项
Windows Server 2012 NIC Teaming介绍及注意事项 转载自:http://www.it165.net/os/html/201303/4799.html Windows Ser ...
- Linux下服务器端开发流程及相关工具介绍(C++)
去年刚毕业来公司后,做为新人,发现很多东西都没有文档,各种工具和地址都是口口相传的,而且很多时候都是不知道有哪些工具可以使用,所以当时就想把自己接触到的这些东西记录下来,为后来者提供参考,相当于一个路 ...
- JavaScript var关键字、变量的状态、异常处理、命名规范等介绍
本篇主要介绍var关键字.变量的undefined和null状态.异常处理.命名规范. 目录 1. var 关键字:介绍var关键字的使用. 2. 变量的状态:介绍变量的未定义.已定义未赋值.已定义已 ...
- HTML DOM 介绍
本篇主要介绍DOM内容.DOM 节点.节点属性以及获取HTML元素的方法. 目录 1. 介绍 DOM:介绍DOM,以及对DOM分类和功能的说明. 2. DOM 节点:介绍DOM节点分类和节点层次. 3 ...
- HTML 事件(一) 事件的介绍
本篇主要介绍HTML中的事件知识:事件相关术语.DOM事件规范.事件对象. 其他事件文章 1. HTML 事件(一) 事件的介绍 2. HTML 事件(二) 事件的注册与注销 3. HTML 事件(三 ...
- HTML5 介绍
本篇主要介绍HTML5规范的内容和页面上的架构变动. 目录 1. HTML5介绍 1.1 介绍 1.2 内容 1.3 浏览器支持情况 2. 创建HTML5页面 2.1 <!DOCTYPE> ...
- ExtJS 4.2 介绍
本篇介绍ExtJS相关知识,是以ExtJS4.2.1版本为基础进行说明,包括:ExtJS的特点.MVC模式.4.2.1GPL版本资源的下载和说明以及4种主题的演示. 目录 1. 介绍 1.1 说明 1 ...
随机推荐
- SQL中truncate table和delete的区别
truncate table table_name delete from table_name drop table table_name truncate table在功能上与不带where子句的 ...
- sql数据库出现可疑
USE master GO SP_CONFIGURE 'allow updates',1 RECONFIGURE WITH OVERRIDE GO UPDATE SYSDATABASES SET ST ...
- iOS:多个单元格的删除(方法二):
前面介绍了万无一失的方法一,这里介绍删除单元格的第二种方式,通过删除单元格中的内容的方式进行操作:(但是这种情况有一个小的弊端,由于单元格重用机制,如果单元格内容一样时,标记的存在会造成误删) 删除前 ...
- STL中关联式容器的特性
1.map 代码如下: /* * map_1.cpp * * Created on: 2013年8月6日 * Author: Administrator */ #include <iostrea ...
- akka模块
模块 Akka的模块化做得非常好,它为不同的功能提供了不同的Jar包. akka-actor-2.0.jar – 标准Actor, 有类型Actor,等等 akka-remote-2.0.jar – ...
- </p>标题设置
333333333333333333333333333333 border-top-left-radius: 3px; border-top-right-radius: 3px; border-bot ...
- Dragon of Loowater
option=com_onlinejudge&Itemid=8&page=show_problem&problem=2267" style="color:b ...
- nginx的 CPU參数worker_processes和worker_cpu_affinity使用说明
Nginx默认没有开启利用多核CPU,我们能够通过添加worker_cpu_affinity配置參数来充分利用多核CPU.CPU是任务处理,计算最关键的资源,CPU核越多.性能就越好. worker_ ...
- Oracle 之 常用函数
SQL语句根据参数的不同,分为单行函数 和 多行函数. [1] 单行函数:输入是一行,输出也是一行: [2] 多行函数:输入多行数据,输出一个结果. 在执行时,单行函数是检索一行处理一次,而多行函数是 ...
- Reduce 优化(mapr)
1.合理设计桶的大小,插入桶的时候,桶的数目和reduce的数目一致,结合map的输出大小合理设置桶的大小,否则在reduce阶段就会非常慢. 2.查看reduce的copy的速率,如果map out ...