____ _ _ _| __ ) __ _ ___ ___ ___| |__ ___| | || _ \ / _` / __|/ _ \/ __| '_ \ / _ \ | || |_) | (_| \__ \ __/\__ \ | | | __/ | ||____/ \__,_|___/\___||___/_| |_|\___|_|_|
BaseShell使用教程
我写这套框架的初衷在于丰富自己的shell脚本库,以期在写少量简单脚本辅助开发时候能够像使用Java类库一样方便。同时为了写出类Java的工具类,这会强迫自己深入学习Java的源代码。
所以Java是我的第一语言,Scala作为第二语言,忠实的shell粉,但不排除其余编程语言。这是我对编程语言的态度。
BaseShell相似于Java语言的SDK是为Shell脚本语言提供的一套工具库,涵盖多种类Java工具的实现
.├── Annotation #函数参数校验脚本| └── BaseAnnotation.sh├── Collection #集合解决脚本| ├── BaseArrayList.sh| ├── BaseHasMap.sh| └── BaseMap.sh├── Concurrent #并发控制脚本| ├── BaseLock.sh| ├── BaseThreadPool.sh| └── BaseThreadPoolExecutor.sh├── Constant #自己设置常量| └── BaseConstant.sh├── Date #日期解决脚本| ├── BaseLocalDate.sh| ├── BaseLocalDateTime.sh| ├── BaseLocalTime.sh| ├── BaseMonth.sh| ├── BaseTimeUnit.sh| ├── BaseTimestamp.sh| └── BaseWeek.sh├── File #文件解决脚本| ├── BaseFile.sh| └── BaseTable.sh├── Lang | ├── BaseMath.sh| ├── BaseObject.sh| └── BaseString.sh├── Log #日志脚本| └── BaseLog.sh├── Ssh #ssh工机具| └── BaseSsh.sh├── Starter #包导入辅助脚本| ├── BaseDateTimeStarter.sh| ├── BaseEnd.sh| ├── BaseHeader.sh| ├── BaseStarter.sh| ├── BaseTestEnd.sh| └── BaseTestHeader.sh├── Test #单元测试脚本| ├── BaseAnnotationTest.sh| ├── BaseArrayListTest.sh| ├── BaseLocalDateTest.sh| ├── BaseLocalDateTimeTest.sh| ├── BaseLocalTimeTest.sh| ├── BaseLockTest.sh| ├── BaseLogTest.sh| ├── BaseMapTest.sh| ├── BaseMiniTest.sh| ├── BaseObjectTest.sh| ├── BaseRandomTest.sh| ├── BaseStringTest.sh| ├── BaseTableTest.sh| ├── BaseThreadPoolExecutorTest.sh| ├── BaseThreadPoolTest.sh| ├── BaseUuidTest.sh| └── table├── Utils #辅助工具脚本| ├── BaseCodec.sh| ├── BaseRandom.sh| └── BaseUuid.sh├── Banner #项目头图├── config.sh #项目配置文件├── init.sh #项目初始化脚本├── BaseShellMini.sh #常用最小引入脚本└── readme.md #项目详情
目前还在完善当中。旨在简化shell脚本的编写、提高shell脚本的健壮性。丰富的脚本库可以大大减少shell脚本编写的难度,其类Java的实现方式可以使得面向对象范式的程序员很快的了解并使用。与之相配套的我会出一篇《Shell编程规约》,以期规范Shell脚本程序员的书写习惯。
cd ~mkdir shell && cd shell #新建一个script目录用于存放所有的脚本git clone chen-shang/BaseShell.gitsh $(pwd)/BaseShell/init.sh
根据提醒输入 project[项目目录] 和 module[模块名称]
看到如下输出,则新建项目成功
> sh $(pwd)/BaseShell/init.shproject[项目目录]:com.baseshell.learnmodule[模块名称]:Script./../../com.baseshell.learn├── BaseShell -> /Users/chenshang/shell/BaseShell└── Script ├── Resources ├── Service | └── Main.sh ├── Test ├── Utils ├── config.sh └── readme.md6 directories, 3 files
【强制】运行shell脚本要到脚本目录下执行
cd com.baseshell.learn/Script/Service/sh Main.sh
看到如下输出,说明项目运行ok,之后可以愉快的写脚本了
____ ____ _ _ _| __ ) __ _ ___ ___/ ___|| |__ ___| | || _ \ / _` / __|/ _ \___ \| '_ \ / _ \ | || |_) | (_| \__ \ __/___) | | | | __/ | ||____/ \__,_|___/\___|____/|_| |_|\___|_|_|hello world
./../../com.baseshell.learn 项目目录├── BaseShell -> /Users/chenshang/shell/BaseShell BaseShell的源码软链└── Script 模块目录:一般建议大写,代表一个Shell模块,里面专门是针对某个模块儿的脚本 ├── Resources 资源目录:资源目录: 一般放少量文本文件、图片、csv等非脚本文件 ├── Service 项目目录:项目相关脚本所在的文件,假如想要写少量辅助的脚本,建议与Service同级创立一个文件夹来写 | └── Main.sh ├── Test 测试目录:对脚本中的函数进行单元测试的脚本 ├── Utils 工具目录:工具类 ├── config.sh 配置文件:项目的配置文件包括 头图、日志级别等以及少量项目中用到的配置项 └── readme.md 形容文件:项目名称、项目详情等等└── Module2 模块二6 directories, 3 files
BaseShell相当于Java的JDK.
配置文件:
【推荐】config.sh 脚本中尽量之定义变量,不要定义函数或者可执行命令,类比Java项目中的properties
形容文件: 项目的形容文件
脚本应该怎样写-示例
#!/usr/bin/env bash# shellcheck disable=SC1091,SC2155#===============================================================import="$(basename "${BASH_SOURCE[0]}" .sh)_$$"if [[ $(eval echo '$'"${import}") == 0 ]]; then return; fieval "${import}=0"#===============================================================source ./../../BaseShell/Starter/BaseHeader.shsource ./../config.sh#导入工具包#===============================================================================#业务代码main(){ echo "hello world"}#===============================================================================source ../../BaseShell/Starter/BaseEnd.sh
对应上面示例脚本第一行
【推荐】首行写法 #!/usr/bin/env bash
对应上面示例脚本第二行
【强制】写好脚本后,使用shellcheck进行语法检查
对应上面示例脚本3-7行
#===============================================================import="$(basename "${BASH_SOURCE[0]}" .sh)_$$"if [[ $(eval echo '$'"${import}") == 0 ]]; then return; fieval "${import}=0"#===============================================================
这段代码的作用是为了预防 像 A脚本引用B脚本,B脚本又引用A脚本导致的循环引用问题。其原理是以文件名定义一个变量,引用过之后变量的值设置为0,再次引用的时候直接return,注意再次引用的时候是直接return不是exit. 详细用法,后面会有开展。
这段代码目前是我能想到的最精简的方式,不排除以后有更优的方案。
#===============================================================source ./../../BaseShell/Starter/BaseHeader.shsource ./../config.sh#导入工具包#===============================================================================
引用第三方脚本使用source命令,相当于Java的import关键字source ./../../BaseShell/Starter/BaseHeader.sh
,source ./../config.sh
这两个是必需要引用的,且放在所有引用的开头。source ./../config.sh
则是为了由于当前项目的配置文件,我们对项目的全局配置变量都在这里面定义source ./../../BaseShell/Starter/BaseHeader.sh
目的是引入BaseShell框架. 相当于引用下面四个包
source ./../../BaseShell/Lang/BaseObject.shsource ./../../BaseShell/Log/BaseLog.shsource ./../../BaseShell/Annotation/BaseAnnotation.shsource ./../../BaseShell/Lang/BaseString.sh
【推荐】Service包中的业务代码都推荐写入main
#===============================================================================source ../../BaseShell/Starter/BaseEnd.sh
这样直接执行脚本的时候,会先寻觅脚本里面的main函数去执行,相似运行一个Java Class类中的main方法
我们规定项目目录最大深度为2层,也就是不允许在Service同级的目录下在创立目录而后在里面写脚本
引用包使用source命令source 第三方脚本
会使第三方脚本从头到尾加载一遍,遇到函数就加载函数、遇到变量就加载变量、遇到可执行的命令就会执行,这个命令其实就是把第三方脚本定义的函数、全局变量加载到当前脚本的上下文中
这里推荐使用相对路径,由于使用绝对路径,IDEA无法进行代码提醒,也是醉了
示例
假如想引入日期相关的函数 source ./../../BaseShell/Date/BaseLocalDate.sh
假如想引入文件相关的函数 source ./../../BaseShell/File/BaseFile.sh
.├── Annotation #函数参数校验脚本| └── BaseAnnotation.sh
默认自动引入,此包下的工具是用来进行函数参数校验的,相似Spring中的Validate的功能。一旦参数校验没有通过则会终止函数的执行。 所有方法都是以 _
开头的,相似于Java中的@
曾想用@开头,但发现@在shell中属于特殊字符,不允许出现在函数命中,斟酌再三选择了_
方法 | 表头 | 备注 |
---|---|---|
_NotBlank | 入参数不为空(空或者空字符串) | - |
_Natural | 入参数为自然数(0,1,2,3...) | - |
_Min | 最大不得小于此最小值 | - |
_Max | 最大不得超过此最大值 | - |
示例
# 将文件内容读进内存function ssh_checkLogin(){ _NotBlank "$1" "ip can not be null" && _NotBlank "$2" "port can not be null" && _NotBlank "$3" "user can not bull" && _NotBlank "$4" "pass can not bull" local ip=$1 ;local port=$2 ;local user=$3 ;local pass=$4}
一般函数的参数校验,我一般和函数也在一行上,第二行用具体的变量名接收参数。上面的函数假如有某个参数没有传,函数会异常退出,下面的代码也不会执行。
├── Collection| ├── BaseArrayList.sh| ├── BaseHasMap.sh | └── BaseMap.sh
默认自动引入,此包下的工具是用来对集合 和 Map 进行操作的。
方法 | 表头 | 备注 |
---|---|---|
new_arrayList | 新建一个list | - |
list_add | 增加元素 | - |
list_set | 设置元素 | - |
list_removeByIndex | 按照下标移除元素 | - |
list_removeByValue | 按照值移除元素 | - |
list_get | 按照下标获取元素 | - |
list_forEach | 对列表中的每一个元素都进行操作 | - |
list_size | 获取当前list的元素个数 | - |
list_isEmpty | 判断当前list能否为空 | - |
list_contains | 判断当前list能否包含某元素 | - |
list_clear | 清空当前list | - |
list_indexOf | 获取指定元素的下标 | 从前往后第一个 |
list_lastIndexOf | 获取指定元素的下标 | 从后往前第一个 |
list_sub | 截取指定下标的元素 | - |
list_copy | 赋值一个新的数组 | - |
list_values | 获取元素的值列表 | - |
list_mapper | 最每一个元素进行操作并返回一个新的列表 | - |
list_reducer | 聚类操作 | - |
source ./../../BaseShell/Starter/BaseHeader.sh
默认会引入Log框架source ./../../BaseShell/Log/BaseLog.sh
无需手动引入
两个系统默认配置,在 config.sh
配置文件中,客户可根据自己的意愿修改
# 日志记录位置LOG_DIR="${HOME}/.baseshell"# 日志级别LOG_LEVEL=SYSTEM
支持固定文本log_debug "要记录的日志内容"支持字符串插值log_debug "要记录的日志内容. key=${key},value=${value}"支持多参数log_debug "要记录的日志内容." "1=$1" "2=$2"支持函数log_debug "要记录的日志内容." "now=$(date)" "index=$((i++))"
Log 包里面有8个方法
方法 | 配置文件配置 | 说明 |
---|---|---|
log_debug | LOG_LEVEL=DEBUG | 打印DEBUG级别的日志 |
log_info | LOG_LEVEL=INFO | 打印INFO级别的日志,输出颜色为白色 |
log_success | LOG_LEVEL=INFO | 打印INFO级别的日志,输出颜色为绿色 |
log_fail | LOG_LEVEL=INFO | 打印INFO级别的日志,输出颜色为红色,并退出当前进程 |
log_warn | LOG_LEVEL=WARN | 打印WARN级别的日志,输出颜色为灰色 |
log_error | LOG_LEVEL=ERROR | 打印ERROR级别的日志,输出颜色为红色 |
log_system | LOG_LEVEL=SYSTEM | 打印SYSTEM级别的日志,输出颜色为白色,是BaseShell使用的日志输出格式 |
log_trace | 只会记录日志到文件,不会打印到控制台 |
执行Test下的测试用例,输出如下
未完待续。。。明天再写