最近疫情比较严重,大家面临了长期呆在家中,很多人已经玩无可玩,刷无可刷的地步。此时不正是最好的充电时刻吗?
我们先看看百度百科怎么说:
脚本(Script)是一种批处理文件的延伸,是一种纯文本保存的程序,一般来说的计算机脚本程序是确定的一系列控制计算机进行运算操作动作的组合,在其中可以实现一定的逻辑分支等。—— 百度百科
我们可以看到这些关键词:批处理文件的延伸,纯文本,实现一定逻辑。简单概述来说就是,具有一系列处理逻辑的纯文本程序!
区别 脚本ShellJavaScriptPython编辑器文本编辑器IDE/文本编辑器IDE/文本编辑器解释器Bourne Shell/Bourne Again Shell由浏览器解释执行Python 解释器语言无专属脚本语言JavaScriptPython运行平台Linux/安装了 bash 的计算机运行于浏览器运行在有 Python 环境的计算机
前面我们已经对什么是脚本有了一个基本的概念,本章节重点对 Shell 脚本的语法进行讲解!
答案:并不是一个东西!二者不能混为一谈。Shell 是一个用 C 编写的程序,是用户使用 Linux 的一个媒介,用户可以通过 Shell 来访问 Linux 内核服务。 而 Shell 脚本是为 Shell 而编写的程序。
学习任何一门语言,入门经典必然是 hello world! 来看一下第一个 Shell 入门程序。
登陆 Linux,在你的当前目录下创建一个 hello.sh:
[root@VM_0_2_centos sskjdata]# vim hello.sh
注:不熟悉 vi/vim 的小伙伴可以去百度一下哦,非常简单!
然后输入一下内容:
#!/bin/bashecho "Hello World !"
非常简单,就两行代码!第一行 #! 是固定写法,是一种标记,作用是告诉系统,这个脚本使用什么解释器来执行。
因为 Linux 系统通常拥有 Bourne Shell/Bourne Again Shell 两种,即 sh 和 bash。通常这两者可以不作区分,所以 #!binsh 和 #!binbash 两种写法都是可以的。
#! 后面的 binbash 是告诉系统用哪个解释器,以及解释器的地址在哪里。
实际的命令就一条,就是 echo 'Hello World!',echo 代表输出打印,就像其他语言的打印语句一样。比如 C 语言的 print。
这样一个简单的入门脚本程序就编写好了,将上面代码保存,接下来我们赋予它执行权限:chmod +x hello.sh。
ll 查看,用颜色区分即可
一般是切换到脚本所在目录,./hello.sh 即可:
[root@VM_0_2_centos sskjdata]# ./hello.sh Hello World!
这样脚本就执行成功了!超简单。
本节讲述日常开发中,经常用到的语法。
定义变量:无语声明,直接赋值后使用。
school="希望小学"
变量命名规范:
bash 关键字大全:
alias [-p] [名称[=值] ... ] logout [n] bg [任务声明 ...] mapfile [-n 计数] [-O 起始序号] [-s 计数] [-t] [-u fd> bind [-lpvsPVS] [-m 键映射] [-f 文件名] [-q 名称] [-u > popd [-n] [+N | -N] break [n] printf [-v var] 格式 [参数] builtin [shell 内嵌 [参数 ...]] pushd [-n] [+N | -N | 目录] caller [表达式] pwd [-LP] case 词 in [模式 [| 模式]...) 命令 ;;]... esac read [-ers] [-a 数组] [-d 分隔符] [-i 缓冲区文字] [> cd [-L|[-P [-e]]] [dir] readarray [-n 计数] [-O 起始序号] [-s 计数] [-t] [-u > command [-pVv] 命令 [参数 ...] readonly [-aAf] [name[=value] ...] or readonly -p compgen [-abcdefgjksuv] [-o 选项] [-A 动作] [-G 全局模> return [n] complete [-abcdefgjksuv] [-pr] [-DE] [-o 选项] [-A 动作] [> select NAME [in 词语 ... ;] do 命令; done compopt [-o|+o 选项] [-DE] [名称 ...] set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...] continue [n] shift [n] coproc [名称] 命令 [重定向] shopt [-pqsu] [-o] [选项名 ...] declare [-aAfFgilrtux] [-p] [name[=value] ...] source 文件名 [参数] dirs [-clpv] [+N] [-N] suspend [-f] disown [-h] [-ar] [任务声明 ...] test [表达式] echo [-neE] [参数 ...] time [-p] 管道 enable [-a] [-dnps] [-f 文件名] [名称 ...] times eval [参数 ...] trap [-lp] [[参数] 信号声明 ...] exec [-cl] [-a 名称] [命令 [参数 ...]] [重定向 ...] 真 exit [n] type [-afptP] 名称 [名称 ...] export [-fn] [名称[=值] ...] 或 export -p typeset [-aAfFgilrtux] [-p] name[=value] ... 伪 ulimit [-SHacdefilmnpqrstuvx] [限制] fc [-e 编辑器名] [-lnr] [起始] [终结] 或 fc -s [模�> umask [-p] [-S] [模式] fg [任务声明] unalias [-a] 名称 [名称 ...] for 名称 [in 词语 ... ] ; do 命令; done unset [-f] [-v] [名称 ...] for (( 表达式1; 表达式2; 表达式3 )); do 命令; done until 命令; do 命令; done function 名称 { 命令 ; } 或 name () { 命令 ; } variables - 一些 shell 变量的名称和含义 getopts 选项字符串 名称 [参数] wait [编号] hash [-lr] [-p 路径名] [-dt] [名称 ...] while 命令; do 命令; done help [-dms] [模式 ...] { 命令 ; }
使用变量:在变量前使用 $ 符号即可。
school="希望小学"echo $school
只读变量:使用 readonly 关键字即可。
school="希望小学"readonly schoolecho $school
删除变量:使用 unset 关键字即可。
school="希望小学"unset school
单引号:原样输出,变量无效。
webname='this is a web'
双引号:可以和变量一起使用。
web='diqiyuzhou'str="welcome to "$web"! "echo -e $str
输出:
welcome to "diqiyuzhou"!
获取字符串长度:
string="abcd"echo ${#string}
输出:
4
截取字符串:
string="jiuyuechangan is a great author"echo ${string:1:4} # 输出 iuyu
bash 仅支持一维数组。
定义数组:
arr=(1,2,3,4)
获取数组:
// 获取单个数组元素value=${arr[0]}// 获取所有数组元素all=${arr[@]}
获取数组长度:
// 方式1len=${#arr[@]}// 方式2len2=${#arr[*]}
# 这是单行注释
多行注释:
:<
Shell 可使用以下运算符
bash 不支持简单的算数运算符,需要用其他命令实现。
或者在 [] 中执行基础运算,a 为 1,b 为 2:
运算符说明举例+加法expr $a + $b 结果为 3。-减法expr $a - $b 结果为 -1。*乘法expr $a * $b 结果为 2。/除法expr $b/$a 结果为 2。%取余expr $b % $a 结果为 0。=赋值a=$b 将把变量 b 的值赋给 a。==相等。用于比较两个数字,相同则返回 true。[ $a == $b ] 返回 false。!=不相等。用于比较两个数字,不相同则返回 true。[ $a != $b ] 返回 true。
注意:条件表达式要放在方括号之间,并且要有空格,例如: [$a==$b] 是错误的,必须写成 [ $a == $b ]。
实战:
#!/bin/bash# 九月长安a=1b=2result=`expr $a + $b`echo "a + b : $result"result=`expr $a - $b`echo "a - b : $result"result=`expr $a * $b`echo "a * b : $result"result=`expr $b/$a`echo "b/a : $result"
输出:
a + b : 3a - b : -1a * b : 2b/a : 2
if else
if conditionthen command1 command2 ... commandN fi
if else-if else
if condition1then command1elif condition2 then command2else commandNfi
test 关键字:检查条件是否成立。
for 循环
for var in item1 item2 ... itemNdo command1 command2 ... commandNdone
while 语句
while conditiondo commanddone
语法:
[ function ] funname [()]{ action; [return int;]}
示例:
// 定义方式1:function good(){ command;}// 定义方式2:good(){ command}
使用语法:
命令 说明command > file 将输出重定向到 file。command < file 将输入重定向到 file。command >> file 将输出以追加的方式重定向到 file。n > file 将文件描述符为 n 的文件重定向到 file。n >> file 将文件描述符为 n 的文件以追加的方式重定向到 file。n >& m 将输出文件 m 和 n 合并。n
已经具备以上基础,则可以自己编写脚本了。下面我们来写一个简单的 Shell 脚本。
#!/bin/bash# 实战编写shell脚本var1=$[1*2]var2=$[1+1]if test $[var1] -eq $[var2]then echo '相等!'else echo '不相等!'fi
执行查看结果:
[root@VM_0_2_centos sskjdata]# ./hello.sh 相等!
我们这里演示 Spring Boot 项目的打包部署。
1. 使用 Maven 插件打包项目。
选择 Lifecycle –> 双击 clean:
[INFO] Scanning for projects...[INFO] [INFO] --------------< com.sskjdata.datacenter:data-governance >---------------[INFO] Building data-governance 1.0.0-RELEASE[INFO] --------------------------------[ jar ]---------------------------------[INFO] [INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ data-governance ---[INFO] Deleting /Users/jason/App/workspace/data-governance/target[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time: 1.395 s[INFO] Finished at: 2020-02-07T13:46:40+08:00[INFO] ------------------------------------------------------------------------
看到 BUILD SUCCESS 即 clean 成功。
接下来再双击 install:
[INFO] Copying twill-discovery-core-0.6.0-incubating.jar to /Users/jason/App/workspace/data-governance/target/lib/twill-discovery-core-0.6.0-incubating.jar[INFO] Copying twill-zookeeper-0.6.0-incubating.jar to /Users/jason/App/workspace/data-governance/target/lib/twill-zookeeper-0.6.0-incubating.jar[INFO] Copying tephra-hbase-compat-1.0-0.6.0.jar to /Users/jason/App/workspace/data-governance/target/lib/tephra-hbase-compat-1.0-0.6.0.jar[INFO] Copying hive-service-rpc-2.1.0.jar to /Users/jason/App/workspace/data-governance/target/lib/hive-service-rpc-2.1.0.jar[INFO] Copying libthrift-0.9.3.jar to /Users/jason/App/workspace/data-governance/target/lib/libthrift-0.9.3.jar[INFO] Copying zookeeper-3.4.6.jar to /Users/jason/App/workspace/data-governance/target/lib/zookeeper-3.4.6.jar[INFO] Copying log4j-1.2.16.jar to /Users/jason/App/workspace/data-governance/target/lib/log4j-1.2.16.jar[INFO] Copying jline-0.9.94.jar to /Users/jason/App/workspace/data-governance/target/lib/jline-0.9.94.jar[INFO] Copying netty-3.7.0.Final.jar to /Users/jason/App/workspace/data-governance/target/lib/netty-3.7.0.Final.jar[INFO] Copying curator-framework-2.6.0.jar to /Users/jason/App/workspace/data-governance/target/lib/curator-framework-2.6.0.jar[INFO] Copying curator-client-2.6.0.jar to /Users/jason/App/workspace/data-governance/target/lib/curator-client-2.6.0.jar[INFO] Copying spring-boot-configuration-processor-2.1.0.RELEASE.jar to /Users/jason/App/workspace/data-governance/target/lib/spring-boot-configuration-processor-2.1.0.RELEASE.jar[INFO] Copying ganymed-ssh2-262.jar to /Users/jason/App/workspace/data-governance/target/lib/ganymed-ssh2-262.jar[INFO] Copying jsch-0.1.54.jar to /Users/jason/App/workspace/data-governance/target/lib/jsch-0.1.54.jar[INFO] [INFO] --- spring-boot-maven-plugin:2.1.0.RELEASE:repackage (repackage) @ data-governance ---[INFO] Replacing main artifact /Users/jason/App/workspace/data-governance/target/data-governance-1.0.0-RELEASE.jar[INFO] [INFO] --- spring-boot-maven-plugin:2.1.0.RELEASE:repackage (default) @ data-governance ---[INFO] Replacing main artifact /Users/jason/App/workspace/data-governance/target/data-governance-1.0.0-RELEASE.jar[INFO] [INFO] --- maven-install-plugin:2.5.2:install (default-install) @ data-governance ---[INFO] Installing /Users/jason/App/workspace/data-governance/target/data-governance-1.0.0-RELEASE.jar to /Users/jason/.m2/repository/com/sskjdata/datacenter/data-governance/1.0.0-RELEASE/data-governance-1.0.0-RELEASE.jar[INFO] Installing /Users/jason/App/workspace/data-governance/pom.xml to /Users/jason/.m2/repository/com/sskjdata/datacenter/data-governance/1.0.0-RELEASE/data-governance-1.0.0-RELEASE.pom[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time: 27.828 s[INFO] Finished at: 2020-02-07T13:48:44+08:00[INFO] ------------------------------------------------------------------------
看到 BUILD SUCCESS 即打包成功。
然后将 jar 包上传到服务器,使用命令:关闭之前运行的 jar 包,然后替换 jar 包,然后启动 jar 包查看日志。
手动部署有以下步骤:
以上就是最简单的手动部署步骤,但是还是可以看出,比较依赖人工。
关闭项目脚本
// 我们先创建一个专门存放脚本的目录mkdir bin// 然后在该目录下创建脚本cd bin// 创建启动脚本vim stop.sh
编写 stop.sh 脚本内容
因为每次打包的 jar 包名称都是固定的,所以我们可以这样编写:
#!/bin/bash# 实现关闭杀死部署进程echo "正在关闭项目, 确保jar包名称以data-governance开头!"kill -9 $(ps -ef | grep data-governance | grep -v grep | awk '{print $2}')echo "关闭完成!"
使用 echo 提示用户,正在执行关闭项目操作。
这样脚本就写好了。不但更加人性化,而且更加方便快捷。
查看项目状态脚本
#!/bin/bash# 检查项目是否正在运行!var=$(ps -ef | grep data-governance | grep -v grep | awk '{print $2}')len=${#var}if test $[len] -gt 0then echo '项目正在运行...'else echo '项目已停止'fi
这样检查脚本也写好了。
启动项目脚本
#!/bin/bash# 大数据项目部署 启动脚本echo '正在启动数据治理项目...'nohup /usr/local/jdk/jdk1.8.0_161/bin/java -jar data-governance-1.0.0-RELEASE.jar &var=$(ps -ef | grep data-governance | grep -v grep | awk '{print $2}')len=${#var}if test $[len] -gt 0then echo '启动成功!'else echo '启动失败,请检查jar包是否出错'fi
然后我们将这三个启动脚本赋予执行权限。
脚本部署步骤:
是不是爽的雅痞?./bin/st 然后按 tab 自动补齐,然后直接会车!那速度,那效率,那体验。以后部署也就几个会车的事情,简单,高效!
在 IDE 上,打包上传觉得繁琐?我们直接看一个现成的例子。
服务器运行该脚本前提:
脚本模版内容:
#!/bin/bashselect_branch=$1servers=("data-xxx" "data-xxx")srcDir='/usr/local/xxxxxx/'# 拉取代码gitclone(){ if [[ "$select_branch" = "master" ]]; then pullcode; fi}# git 拉取 master 分支pullcode(){ # 切换目录 cd /usr/local # 删除就文件 rm -rf xxx #克隆代码 git clone [email protected]:xxxx/xxxx.git cd xxxx # 切换分支 git checkout master}# clean 和 installmvncomplier(){ for dir in ${servers[@]} do dirFile=$srcDir$dir; if [ -d $dirFile ]&&[[ $dir != script* ]]&&[[ $dir != data* ]]; then cd $dirFile; mvn clean install; fi done;}# 关闭项目群,一个项目可能由多个子项目构成stop(){ for server in ${servers[@]} do pid=`ps -ef|grep $server|grep -v "grep"|awk '{print $2}'` kill -9 $pid npid=`ps -ef|grep $server|grep -v "grep"|awk '{print $2}'` if [ "$npid" == "" ]; then echo -e "关闭项目成功!" else echo -e "寡女哦项目失败" fi done}i=10000# 启动项目start(){ for serv in ${servers[@]} do cd $srcDir$serv/target; nohup java -jar $serv.jar & i=$(($i+1)) npid=`ps -ef|grep $serv|grep -v "grep"|awk '{print $2}'` if [ "$npid" != "" ]; then echo -e "$serv 启动成功!" else echo -e "$serv 未启动!" fi done}echo -e ">>>>>>> 更新代码 >>>>>>>"gitclone;echo -e ">>>>>>> 编译中:>>>>>>>"mvncomplier;echo -e ">>>>>>> 正在重启项目 <<<<<<<
这样就能一键完成,从拉取代码到重启一系列工作了。是不是很方便呢?
每个项目,每个公司,每个人的情况都是不一样的。我们选择脚本需要因地制宜,依照实际情况而定,不能一味生搬硬套。
例如:
总而言之,我们要根据需求作出更好的部署方案。
这里推荐本人其他 Chat,感兴趣的可以订阅: