一个Linux的tomcat自动部署shell脚本拆解
由于服务器上安装Jenkins实现自动化部署未果,遂找寻替代方法,在网上抄了一份自动部署的脚本。这个脚本是使用shell语言进行编写,而自己此前从未接触过shell编程,于是尝试对此脚本进行逐句拆解,管中窥豹。
#! /bin/sh
echo '####################开始自动部署####################'
path=$(pwd) #当前路径
backupTime=$(date +'%Y%m%d_%H_%M_%S')
tomcatPath=/home/dev/apache-tomcat-8.5.42/ #指定tomcat文件目录名称
cd $tomcatPath/bin || exit #进入tomcat的bin目录
PID=$(ps -fu root|grep tomcat|grep -v grep|awk '{print $2}') # 现场
if [ -z "$PID" ];then
echo "no tomcat process"
else
echo "开始安全结束进程"
kill -15 "$PID"
# ./shutdown.sh #停止tomcat服务
sleep 5
if [ -z "$PID" ];then
echo '进程'"$PID"'已安全结束'
else
echo '进程未安全结束,开始强制结束'
kill -9 "$PID"
echo '进程'"$PID"'已结束'
fi
fi
sleep 1 #休眠1s
cd ../webapps || exit #进入tomcat的webapps目录
rm -fr helloWorld #删除helloWorld文件目录
echo '文件目录删除完成'
mv helloWorld.war ./backup/helloWorld.war."$backupTime" #备份至backup
echo '备份完成'
sleep 1 #休眠1s
cp "$path"/helloWorld.war ./ #复制war到webapps路径下
sleep 1 #休眠1s
cd ../bin || exit
./startup.sh #启动tomcat服务
echo '####################部署结束####################'
脚本很简单,总共才30行。运行时首先要将文件的权限修改为可执行:chmod +x comdfile
,并将文件格式设置为Unix。
-
#! /bin/sh
shell编程是以
#
为注释,但对#! /bin/sh
却不是。"#! /bin/sh"是对shell的声明,说明所用的是哪种类型的shell及其路径所在。(#! /bin/sh
是指此脚本使用/bin/sh来解释执行,#!
是特殊的表示符,其后面跟的是解释此脚本的shell的路径,当然路径不只此一种。) -
echo '####################开始自动部署####################'
echo
命令用于向窗口输出文本,等价于Java里的System.out.println()
,没什么好说的。 -
path=$(pwd) #当前路径
pwd
是Linux中的一个命令,可以用来查看当前工作目录的完整路径。 简单来说,在终端进行操作时,都会有一个当前工作目录。 在不太确定当前位置时,就会使用pwd
来判定当前目录在文件系统内的确切位置。将pwd指代的路径赋值给path变量,在后面切换目录后,path的值仍然不会改变。 -
backupTime=$(date +%Y%m%d_%H_%M_%S)
shell中格式化日期的方法
一)显示系统时间
date
CST中央标准时间——Mon Aug 23 10:52:19 CST 2021date -R
带时区的时间——Mon, 23 Aug 2021 10:52:37 +0800
二)格式化日期
$(date +'%Y-%m-%d %H:%M')
——2021-08-21 10:51回到2天前:
$(date -d '2 day ago' '+%Y/%m/%d %H:%M:%S')
——2021/08/21 10:51:01 -
tomcatPath=/home/dev/apache-tomcat-8.5.42 #指定tomcat文件目录名称
类似于
String a = "Hell World";
没什么好说的。 -
cd $tomcatPath/bin || exit #进入tomcat的bin目录
cd
是一个Linux的基础命令,用来切换目录,未成功则退出脚本。没什么好说的。$
表示后面是变量。两者组合就是进入了/home/dev/apache-tomcat-8.5.42/bin
这个目录。 -
PID=$(ps -fu root|grep tomcat|grep -v grep|awk '{print $2}')
逐层拆解,整体的意思为给
PID
这个变量赋值,$(...)
是吧括号其中的整体作为一个变量看待。然后是括号内的
ps -fu root|grep tomcat|grep -v grep|awk '{print $2}'
。首先,
|
是管道符,作用就如其名,将管道左边命令的结果作为右边命令的基础。ps
是Linux命令,作用是将某个进程显示出来。常用的是ps -ef
和ps -fu
ps -ef
-A 显示所有程序。 -e 列出程序时,显示每个程序所使用的环境变量,效果与-A相同 -f 显示UID,PPIP,C与STIME栏位,用ASCII字符显示树状结构,表达程序间的相互关系。 -u 以用户为主的格式来显示程序状况。
shell命令中,
grep
命令,是对文本行的搜索命令。上图中
ps -ef|grep helloWorld
是明显不存在这样的程序的。但是由于这条命令本身也属于一条进程,所以会将自己查出来。管道符连接中有一段是
|grep -v grep|
,-v
可以过滤掉包含grep的进程行,只留下目标内容。ps -fu root
由于此条命令中包含-u,所以可以携带需要查找的用户id。
awk '{print $2}'
是管道连接的最后一段,其中awk
是Linux的文本分析语言。这段命令简单来说就是一行一行的读取管道处理结果, 以空格作为分隔符,打印第二个字段。而刚才查出来的第二个字段就是这条进程的PID。 -
if [ -z "$PID" ];then echo "no tomcat process" else kill -15 "$PID" # ./shutdown.sh #停止tomcat服务 sleep 2 if [ -z "$PID" ];then echo '进程'"$PID"'已安全结束' else echo '进程未安全结束,开始强制结束' kill -9 "$PID" echo '进程'"$PID"'已结束' fi fi
这一块放在一起看,
if ... then ... fi
是shell脚本的结构化命令。语法格式分为-
if command;then command;fi
(如果if满足条件然后执行then后面的command) -
if command ... then ...else...fi
(如果if满足条件然后执行then后面的command,否则则执行else后面的command) -
if command ...then ...elif...then...多层elif...fi
(如果满足if判断条件则执行then后的command动作,如果if分别满足elif判断条件则执行elif的判断条件)
需要注意的是,if的判断条件中,使用双括号来进行来进行数值的运算,使用双方括号来进行字符串的比较。
#!/bin/bash # 双括号进行数值运算 num1=10 if (( $num1 + 10 > 20 )) then echo "1" else echo "2" fi # 使用双方括号来进行字符串的比较 if [[ `ymx` != "root" ]] then echo "ymx" fi
比较字符写法:
# 比较字符写法: -eq # 等于 -ne # 不等于 -gt # 大于 -lt # 小于 -le # 小于等于 -ge # 大于等于 -z # 空串 = # 两个字符相等 != # 两个字符不等 -n # 非空串 # 举例: if [ $# -ge 2 ];then echo "你提供的参数过多!" exit 1 else if [ $# -ne 0 ];then if [ $1 = s ];then echo "on e" else echo "two" fi else echo "3" fi fi
上面的例子中,又出现了一个
$#
,而这个属于是shell中的特殊变量。特殊变量
$0 $1 $2 $? $# $@ $*
,这些变量在脚本中可以作为全局变量来使用。名称 说明 $0 脚本名称 $1-9 脚本执行时的参数1到参数9 $? 脚本的返回值 $# 脚本执行时,输入的参数的个数 $@ 输入的参数的具体内容(将输入的参数作为一个多个对象,即是所有参数的一个列表) $* 输入的参数的具体内容(将输入的参数作为一个单词) 另外要注意的是单引号、双引号以及没有引号的区别。
-
单引号
可以说是所见即所得:即将单引号内的内容原样输出,或者描述为单引号里面看见的是什么就会输出什么。
-
双引号
把双引号内的内容输出;
如果内容中有命令、变量等,会先把变量、命令解析出结果,然后再输出最终内容。双引号内命令或变量的写法为
`命令或变量`
或$(命令或变量)
。 -
无引号
把内容输出,可能不会将含有空格的字符串视为一个整体输出;
如果内容中有命令、变量等,会先把变量、命令解析结果,然后再输出最终内容;
如果字符串中带有空格等特殊字符,则不能完整地输出,需要改加双引号。一般连续的字符串,数字,路径等可以用,不过最好全部用双引号替代之。
最后再回过头看这一句,就很清晰了:如果"变量PID"是空为真,则输出“no tomcat process”的语句;否则执行当前目录下的shutdown.sh,即tomcat的结束命令。
-
-
kill -9 $PID
kill
命令是linux的杀掉进程命令。用法:
kill [-s 信号声明|-n信号编号| -信号声明]进程号|任务声明...或kill -l [信号声明]
这里的
-9
就是-n 信号编号。 当不传-*时,默认为-15。
举个不恰当的比喻,-15就好比皇帝给臣子赐死,臣子还可以写一下遗书,料理后事;
-9就好似当庭格杀,不给任何反应机会。
详见:Linux kill、kill-15、kill-9区别
-
sleep 5 #休眠5s
休眠命令,可以关注的一点是它不标记单位时是默认秒为单位,也可以标记单位 s、m、h;其他的没什么好说的。
-
rm -fr helloWorld #删除helloWorld文件目录
rm
命令是一个删除命令,参数
-r
可以删除目录下的所有文件及目录;-f
可以无需逐一确认命令,并可删除唯读文件。 -
mv helloWorld.war ./backup/helloWorld.war."$backupTime" #备份至backup cp "$path"/helloWorld.war ./ #复制war到webapps路径下
mv
命令将旧的war包移动到同级目录下的backup目录,并在文件名后面加上时间变量,用作备份。cp
命令将最开始的脚本运行目录下的新war包复制到 当前目录下。 -
cd ../bin || exit ./startup.sh #启动tomcat服务
切换到bin目录下,执行tomcat的启动脚本。
至此脚本执行结束。
更新
#! /bin/sh
echo '####################开始自动部署####################'
path=$(pwd) #当前路径
warName=$(ls -- *helloWorld*.war) # 打包换成了自带时间戳的格式,所以增加了获取新包的代码
tomcatPath=/home/dev/apache-tomcat-8.5.42/ #指定tomcat文件目录名称
cd "$tomcatPath"/bin || exit #进入tomcat的bin目录
PID=$(ps -fu root|grep tomcat|grep -v grep|awk '{print $2}') # 现场
if [ -z "$PID" ];then
echo "没有正在运行的服务,开始进行下一步"
else
echo "开始结束进程"
kill -9 "$PID"
echo '进程'"$PID"'已结束'
fi
sleep 1 #休眠1s
cd ../webapps || exit #进入tomcat的webapps目录
rm -fr "$(find ./ -name "*helloWorld*" -type d)" #删除解压过的文件目录
echo '文件目录删除完成'
sleep 1 #休眠1s
cp "$path"/helloWorld.war ./ #复制war到webapps路径下
sleep 1 #休眠1s
cd ../bin || exit
./startup.sh #启动tomcat服务
echo '####################部署结束####################'
- 由于后面换成了maven打包时,自动打包成带时间戳的格式,类似于
helloWorld-20211111.war
这样,所以里面的war包名就不能写死了。使用了warName=$(ls -- *helloWorld*.war)
,动态获取war包名。 - 由于
kill -15
过于不可靠(太慢了),最终还是换回了kill -9 "$PID"
。 rm -fr "$(find ./ -name "*helloWorld*" -type d)"
删除目录也换成了动态的,使用了find
指令获取当前目录下,名字为*helloWorld*
规则,type(类型)为d(文件夹dir)的目录名。