- Best Shell tool
- 1. 关于BST
- 2. shell控制字符模块
- 2.1 getFontStyle
- 2.2 clearAllProps
- 2.3 getHighlightString
- 2.4 getUnderLineString
- 2.5 getBlinkString
- 2.6 getRDisplayString
- 2.7 getCancelHideString
- 2.8 controlArrowMove
- 2.9 setArrowPosition
- 2.10 clearScreen
- 2.11 saveArrowPosition
- 2.12 readArrowPosition
- 2.13 hideArrow
- 2.14 showArrow
- 2.15 clearPositionAfter
- 2.16 getFmtString
- 2.17 基于控制字符实现一个 进度条 功能
- 3. CommandX语法和语法解析器
- 4. IOStand标准输入输出库
- 5. Tool工具
dev以来需要用--legacy-peer-deps来解决冲突
BST实现的是一个shell & command的操作库,分别提供了shell控制字符串操作、shell终端操作、command parse操作和基础shell功能组件四大能力。
BST期望做到简化命令行编程能力,更快、更简单地开发命令行工具和cli。在后续的介绍过程中,会带大家实现几个命令行小功能,来了解BST开发命令行工具的过程。
该模块提供了生成控制字符的能力。所谓控制字符其实就是改变终端或文件显示的一些行为。一个控制符是由 CONTRL + key 组成的(同时按下)。控制字符同样可以通过转义以八进制或十六进制的方式显示。
获取带字体样式的shell消息,我们可以通过这个能力,获取带字体颜色和背景颜色的shell消息字符串。直接用console或stdout输出这个消息,就可以看到带颜色的文字。
getFontStyle(fontColor:Color, backColor:Color, msg:string):string
关于Color的定义如下TS所示:
type Color = 'black' | 'red' | 'green' | 'yellow' | 'blue' | 'purple' | 'celeste' | 'white';
示例:
const bst = require('best-shell-tool')
console.log(bst.getFontStyle('blue', '', '我是蓝色字'))
console.log(bst.getFontStyle('red', '', '我是红色字'))
console.log(bst.getFontStyle('yellow', 'blue', '我是黄色字蓝色背景'))
输出:
获取清除所有属性的shell消息。通过这个可以清除前文所设置的所有属性样式。
clearAllProps(msg:string):string
示例:
const bst = require('best-shell-tool')
console.log(bst.getFontStyle('blue')+'我是蓝色')
console.log(bst.getFontStyle('blue')+bst.clearAllProps('我的蓝色属性没有被继承过来'))
输出:
获取高亮的shell消息,其实个人感觉就是稍微加粗了一下。
getHighlightString(msg:string):string
示例:
const bst = require('best-shell-tool')
console.log(bst.clearAllProps('我是普通字'))
console.log(bst.getHighlightString('我是高亮字'))
console.log(bst.getFontStyle('red') + bst.getHighlightString('我是红色高亮字'))
输出:
获取下划线的shell消息
getUnderLineString(msg:string):string
示例:
const bst = require('best-shell-tool')
console.log(bst.clearAllProps('我是普通字'))
console.log(bst.getUnderLineString('我是下划线字'))
console.log(bst.getFontStyle('red') + bst.getUnderLineString('我是红色下划线字'))
输出:
获取闪烁字体的shell消息,故名思议,该功能实现了shell文字闪烁,一般在shell交互能力中,用于表示已经选中的选项。
getUnderLineString(msg:string):string
示例:
const bst = require('best-shell-tool')
console.log(bst.clearAllProps('我是普通字'))
console.log(bst.getBlinkString('我是闪烁字'))
console.log(bst.getFontStyle('red') + bst.getBlinkString('我是红色闪烁字'))
获取反显的shell消息,所谓反显,就是模拟文字被选中的状态,一般呈现为 背景=字体颜色, 字体颜色=背景。
getRDisplayString(msg:string):string
示例:
const bst = require('best-shell-tool')
console.log(bst.clearAllProps('我是普通字'))
console.log(bst.getRDisplayString('我是反显状态'))
console.log(bst.getFontStyle('red') + bst.getRDisplayString('我是红色字反显状态'))
输出:
获取消隐的shell消息,消隐的消息在控制台是看不见的,但是占位符是真实存在的,并且文字也是可以真实复制的。
getCancelHideString(msg:string):string
示例:
const bst = require('best-shell-tool')
console.log(bst.clearAllProps('我是普通字'))
console.log(bst.getCancelHideString('我是消隐状态'))
控制shell光标移动的shell消息,通过方向指令和移动数量来控制光标的移动,可以实现在不同位置做输出的功能。
controlArrowMove(direct:Direct, lines:number, msg:string):string
关于Direct的定义如下TS所示:
type Direct = 'up' | 'down' | 'right' | 'left' | '上' | '下' | '左' | '右';
示例:
const bst = require('best-shell-tool')
console.log('00 01 02 03')
console.log('10 11 12 13')
console.log('20 21 22 23')
console.log('30 31 32 33')
console.log(bst.controlArrowMove('上', 2, '我是移动后的'))
输出:
设置shell光标位置的shell消息,和controlArrowMove相比,这个是直接通过指定坐标点来移动光标。
setArrowPosition(x:number | '', y:number | '', msg:string):string
清屏,顾名思义,清除之前屏幕所有的内容。
clearScreen(msg:string):string
保存当前光标位置
saveArrowPosition(msg:string):string
取出之前保存的光标位置
saveArrowPosition(msg:string):string
隐藏光标,就是把shell的那个小黑点隐藏。
hideArrow(msg:string):string
显示光标
showArrow(msg:string):string
清除光标之后这一行的消息。在制作进度条的时候可以用它时时清除一行后的消息,保留之前输出的消息。
clearPositionAfter(msg:string):string
获取格式化字符串。和前面的不同,这个是链式获取一串格式化消息,通过end结束链式调用,拿到格式化消息。其中每次返回的StandOutOperate
操作对象,里面的所有操作链都能和前面的函数一一对应。
function getFmtString(_msg:string):StandOutOperate
关于StandOutOperate的定义如下TS所示:
interface StandOutOperate {
/**
* @description: 附加消息
* @param {string} msg 要附加的消息
* @return {StandOutOperate}
*/
msg?: (msg?:string) => StandOutOperate,
/**
* @description: 结束并获得格式化后的字符
* @return {string}
*/
end?: () => string,
/**
* @description: 设置字体样式
* @param {Color | ''} fontColor 字体颜色
* @param {Color | ''} background 背景色
* @return {StandOutOperate}
*/
setFont?: (fontColor?: Color | '', background?: Color | '', msg?:string) => StandOutOperate,
/**
* @description: 清除所有控制属性
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
clearProps?: (msg?:string) => StandOutOperate,
/**
* @description: 高亮文本
* @param {string} msg 消息
* @return {StandOutOperate}
*/
highlight?: (msg?:string) => StandOutOperate,
/**
* @description: 下划线
* @param {string} msg 消息
* @return {StandOutOperate}
*/
underline?: (msg?:string) => StandOutOperate,
/**
* @description: 闪烁
* @param {string} msg 消息
* @return {StandOutOperate}
*/
blink?: (msg?:string) => StandOutOperate,
/**
* @description: 反显
* @param {string} msg 消息
* @return {StandOutOperate}
*/
rdisplay?: (msg?:string) => StandOutOperate,
/**
* @description: 消隐
* @param {string} msg 消息
* @return {StandOutOperate}
*/
cancelHide?: (msg?:string) => StandOutOperate,
/**
* @description: 控制光标移动
* @param {Direct} direct 移动方向
* @param {number} lines 移动行数
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
arrowMove?: (direct?:Direct, lines?:number, msg?:string) => StandOutOperate,
/**
* @description: 设置鼠标位置
* @param {number | ''} x 横坐标移动距离
* @param {number | ''} y 纵坐标移动距离
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
setArrow?: (x?:number | '', y?:number | '', msg?:string) => StandOutOperate,
/**
* @description: 清屏
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
clear?: (msg?:string) => StandOutOperate,
/**
* @description: 保存光标位置
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
saveArrow?: (msg?:string) => StandOutOperate,
/**
* @description: 读取恢复光标位置
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
readArrow?: (msg?:string) => StandOutOperate,
/**
* @description: 隐藏光标
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
hideArrow?: (msg?:string) => StandOutOperate,
/**
* @description: 显示光标
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
showArrow?: (msg?:string) => StandOutOperate,
/**
* @description: 清除光标所在位置之后这一行的所有内容
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
clearAfter?: (msg?:string) => StandOutOperate,
}
示例:
const bst = require('best-shell-tool')
console.log(
bst.getFmtString('我是普通字体')
.setFont('red', '', '我是红色字体')
.clearProps()
.underline('没想到我带下划线了')
.setFont('blue', '', '我蓝了')
.end()
)
输出:
这里是一个简单的实战教学,基于前文提供的api制作一个简单的进度条
功能。当然,因为这个进度条
工具很常用,BST自带的组件库里面已经封装了进度条
。这里的实现只是为了大家更好的掌握和熟悉BST-控制字符模块
的功能。
示例:
function process (current, total, len = 10) {
const back = bst.getFontStyle('', 'white', ' ')
const active = bst.getFontStyle('', 'green', ' ')
const activeNum = parseInt((current / total) * len, 10)
let processStr = ''
for(let i = 0; i < len; i++) {
if(i < activeNum) processStr += active
else processStr += back
}
console.log(
bst.getFmtString()
.hideArrow()
.arrowMove('上', 1)
.clearAfter(processStr)
.clearProps()
.msg(current)
.msg('/')
.msg(total)
.end()
)
}
let count = 0
let total = 21
console.log('准备加载进度\n')
setTimeout(()=>{
const timer = setInterval(()=>{
if(count === total) clearInterval(timer)
process(count, total, 25)
count++
}, 1000)
}, 1000)
CommandX是作者定义的一种简单命令交互语法,它是一种简化、弱化后的shell命令模式,设计之初的目的是为了解决node开发命令行工具时,希望对用户开放命令交互的愿景,CommandX语法通过cmParse可以将对应的命令解析成命令对象的形式。形式定义如下:
interface ParseStruct {
command?: string,
defaultArgs?: string,
args?: {
[argsName:string]:any
}
}
这一节将简单介绍一些CommandX语法编写,和对应转换成命令对象形式的样例。
-
无参数直接命令
commandX语法
command
转换成js对象后{ args: {}, command: 'command' }
-
带参数命令
commandX语法
command -arg1 你好世界 -arg2 我是参数2
转换成js对象后{ args: { arg1: '你好世界', arg2: '我是参数2' }, command: 'command' }
-
布尔值参数使用
commandX语法
command -arg1 -arg2 arg1是布尔值
转换成js对象后{ args: { arg1: true, arg2: 'arg1是布尔值' }, command: 'command' }
-
默认参数值语法
commandX语法
command 我是默认值 -other 我不是默认值
转换成js对象后{ args: { other: '我不是默认值' }, command: 'command', defaultArgs: '我是默认值' }
commandX语法
command -other 我不是默认值 我是默认值
转换成js对象后{ args: { other: '我不是默认值' }, command: 'command', defaultArgs: '我是默认值' }
-
双引号限定字符串
commandX中,带上双引号的字符串叫作双引号限定字符串,该类型字符串中,反斜杠(\
)和双引号("
)属于特殊字符,需要用转义字符才能使他正确转换。 commandX语法
command "她说:\"我爱你\""
转换成js对象后{ args: {}, command: 'command', defaultArgs: '她说:"我爱你"' }
-
自由非限定字符
commandX中,不被双引号("
)包裹的字符串被称作自由非限定字符,对于这类字符是不能使用双引号("
)和减号(-
)开头。所以在自由非限定字符模式下,提供了unicode直接编码转换。用\U;
的模式可以指定任意一个Unicode对应的字符。比如\65;
就会被转换为字符A
。对于一些特殊字符,可以直接使用\S
标识转换的模式,如常用的空格可以用\space;
转换。标识的对应关系如下:\space;
===\backslash;
===\
\slash;
===/
\semicolon;
===;
commandX语法
command \65;\66;\67;
转换成js对象后{ args: {}, command: 'command', defaultArgs: 'ABC' }
parser函数是BST CommandParse模块提供的CommandX语法解析函数,用它可以将CommandX语法编译成js对象。参数str
是要解析commandX语法,参数mode
是指定报错模式,如果为normal
,使用console.log
进行错误提示。如果为strict
,错误直接抛出。参数isDebugger
是用来调试编译的语法分析,如果为true
,则在每一次意外的语法分析,输出当前语法分析状态机的状态
function parser(str:string, mode:Mode = 'normal', isDebugger:boolean = false):ParseStruct
关于ParseStruct的定义如下TS所示:
interface ParseStruct {
command?: string,
defaultArgs?: string,
args?: {
[argsName:string]:any
}
}
示例:
const bst = require('best-shell-tool')
const res = bst.cmParser.parser('command -arg hello,world')
console.log(res)
输出:
{ args: { arg: 'hello,world' }, command: 'command' }
data2Commandx函数是parser的一个逆向过程,它能将命令对象转换成CommandX语法。
function data2Commandx(data:ParseStruct):string
关于ParseStruct的定义如下TS所示:
interface ParseStruct {
command?: string,
defaultArgs?: string,
args?: {
[argsName:string]:any
}
}
示例:
const bst = require('best-shell-tool')
const data = {
args: { isOpen: true, x: '10', y: '20' },
command: 'command',
defaultArgs: 'hello,world'
}
const res = bst.cmParser.data2Commandx(data)
console.log(res)
输出:
command "hello,world" -isOpen -x "10" -y "20"
formatFree函数是一个用来将自由非限定字符串转化为js字符串,CommandX的parser对于自由非限定字符串就是使用该函数实现。
function formatFree(str:string):string
BST提供了一个标准输入输出管理,用它可以轻松管理shell控制台的输入输出。
IOStand的write函数是输出一条消息到控制台。
write(data:string = ''):boolean
示例:
const bst = require('best-shell-tool')
const iostand = new bst.IOStand()
iostand.write('hello world\n')
此时你会发现控制台输出了hello world
,和console.log
效果类似,不同的是他不会自动在语句后换行。
IOStand的writeChain函数是链式输出一条消息到控制台。里面会返回一条操作链。
writeChain(_msg:string = ''):StandOutOperate
其中StandOutOperate定义如下:
interface StandOutOperate {
/**
* @description: 附加消息
* @param {string} msg 要附加的消息
* @return {StandOutOperate}
*/
msg?: (msg?:string) => StandOutOperate,
/**
* @description: 设置字体样式
* @param {Color | ''} fontColor 字体颜色
* @param {Color | ''} background 背景色
* @return {StandOutOperate}
*/
setFont?: (fontColor?: Color | '', background?: Color | '', msg?:string) => StandOutOperate,
/**
* @description: 清除所有控制属性
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
clearProps?: (msg?:string) => StandOutOperate,
/**
* @description: 高亮文本
* @param {string} msg 消息
* @return {StandOutOperate}
*/
highlight?: (msg?:string) => StandOutOperate,
/**
* @description: 下划线
* @param {string} msg 消息
* @return {StandOutOperate}
*/
underline?: (msg?:string) => StandOutOperate,
/**
* @description: 闪烁
* @param {string} msg 消息
* @return {StandOutOperate}
*/
blink?: (msg?:string) => StandOutOperate,
/**
* @description: 反显
* @param {string} msg 消息
* @return {StandOutOperate}
*/
rdisplay?: (msg?:string) => StandOutOperate,
/**
* @description: 消隐
* @param {string} msg 消息
* @return {StandOutOperate}
*/
cancelHide?: (msg?:string) => StandOutOperate,
/**
* @description: 控制光标移动
* @param {Direct} direct 移动方向
* @param {number} lines 移动行数
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
arrowMove?: (direct?:Direct, lines?:number, msg?:string) => StandOutOperate,
/**
* @description: 设置鼠标位置
* @param {number | ''} x 横坐标移动距离
* @param {number | ''} y 纵坐标移动距离
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
setArrow?: (x?:number | '', y?:number | '', msg?:string) => StandOutOperate,
/**
* @description: 清屏
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
clear?: (msg?:string) => StandOutOperate,
/**
* @description: 保存光标位置
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
saveArrow?: (msg?:string) => StandOutOperate,
/**
* @description: 读取恢复光标位置
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
readArrow?: (msg?:string) => StandOutOperate,
/**
* @description: 隐藏光标
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
hideArrow?: (msg?:string) => StandOutOperate,
/**
* @description: 显示光标
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
showArrow?: (msg?:string) => StandOutOperate,
/**
* @description: 清除光标所在位置之后这一行的所有内容
* @param {string} msg 附加消息
* @return {StandOutOperate}
*/
clearAfter?: (msg?:string) => StandOutOperate,
}
示例:
const bst = require('best-shell-tool')
const iostand = new bst.IOStand()
iostand.writeChain('我是普通字体')
.setFont('red', '', '我是红色字体')
.setFont('yellow', '', '我是黄色字体\n')
start函数是开启命令交互模式,你可以使用oninput
事件来监听输入,注意,当你注册了oninput
事件,那么start就不会启用CommandX命令交互模式。
如下示例,我们注册了oninput事件来监听输入。
示例:
const bst = require('best-shell-tool')
const iostand = new bst.IOStand()
iostand.oninput = (data) => {
console.log('你输入了:', data)
}
iostand.start()
如果我们直接使用start,相当于是一个CommandX交互模式,你需要通过addCommand
函数来注册命令。如下示例所示。
示例:
const iostand = new bst.IOStand()
iostand.addCommand('hello')
.action(()=>{
console.log('hello world')
})
iostand.start()
addCommand是添加一条CommandX命令,这样在开启start交互后,就会根据CommandX寻找已经注册过的action去执行。
addCommand(cmd:string, notes:string = ''):AddCommandOperate
其中AddCommandOperate如下:
interface AddCommandOperate {
/**
* @description: 声明一个参数
* @param {string} argName 参数名称
* @param {string} notes 参数注释
* @return {AddCommandOperate} 返回操作链
*/
arg: (argName:string, notes:string) => AddCommandOperate;
/**
* @description: 声明一个默认参数
* @param {string} notes 参数注释
* @return {AddCommandOperate} 返回操作链
*/
defaultArg: (notes:string) => AddCommandOperate;
/**
* @description: 注册操作函数
* @param {(command:ParseStruct)=>Promise<number>} fn 操作函数
* @return {AddCommandOperate} 返回操作链
*/
action: (fn:(command:ParseStruct)=>Promise<number>)=>AddCommandOperate;
}
示例:
const bst = require('best-shell-tool')
const iostand = new bst.IOStand()
iostand.addCommand('say', '输出一句话到控制台')
.defaultArg('要说的话')
.arg('prefix', '前缀')
.arg('suffyx', '后缀')
.action((cmd)=>{
const prefix = cmd.args.prefix || '';
const suffix = cmd.args.suffix || '';
const content = cmd.defaultArgs || '';
console.log(prefix,content,suffix)
})
iostand.start()
值得注意的是,IOStand里面其实默认注册了一个help命令,通过它可以在控制台查询已经注册过的命令使用方式。
其次,action注册的函数如果返回的是Promise,相当于你是一个异步函数,它会等你执行完成再监听输入。
如果已经存在某个命令,则不能再注册该命令。
listAllCommand函数是列出已经注册过的CommandX命令。
listAllCommand():void
示例:
const bst = require('best-shell-tool')
const iostand = new bst.IOStand()
iostand.addCommand('say', '输出一句话到控制台')
.defaultArg('要说的话')
.arg('prefix', '前缀')
.arg('suffyx', '后缀')
.action((cmd)=>{
const prefix = cmd.args.prefix || '';
const suffix = cmd.args.suffix || '';
const content = cmd.defaultArgs || '';
console.log(prefix,content,suffix)
})
iostand.listAllCommand()
输出:
help
-[默认参数] 要查看帮助的命令(可以不填写)
say 输出一句话到控制台
-[默认参数] 要说的话
-prefix 前缀
-suffyx 后缀
awaitInput是等待一次输入,等待的这次输入不会受到CommandX交互的影响。
awaitInput():Promise<any>
示例:
const bst = require('best-shell-tool')
const iostand = new bst.IOStand();
(async () => {
const inp = await iostand.awaitInput()
console.log('你输入了:', inp)
})();
pause同process.stdin.pause,暂停控制台。
pause():NodeJS.ReadStream & {fd:0;}
resume同process.stdin.resume,恢复控制台输入。
resume():NodeJS.ReadStream & {fd: 0;}
exit同process.exit,退出控制台。
exit():never
release是释放IOStand对象,当不再用到IOStand时候,请一定要使用该函数释放
release():void
Tool提供了一些集成好的小工具,但是目前只提供了一个进度条功能,后续会根据大家的需求进行增加迭代。
process提供的是显示一个进度条能力。
function process(current:number, total:number = 100, len:number = 24):string
示例:
const bst = require('best-shell-tool')
const process = bst.tool.process;
const iostand = new bst.IOStand();
iostand.addCommand('wait', '等待')
.defaultArg('要等待的时间')
.action((cmd)=>{
console.log('')
return new Promise((res) => {
const waitTime = parseInt(cmd.defaultArgs, 10) || 0
let current = 0
const timer = setInterval(()=>{
console.log(process(current, waitTime))
if(current === waitTime) {
clearInterval(timer)
console.log('已经结束等待')
res()
}
current += 1
}, 1000)
})
})
iostand.start()