前言
当需要脚本来管理独立程序的启动、停止、重启、状态查询等功能时,小编为您提供一个模板,便于您参考。
脚本说明
run.sh #用于管理程序执行的脚本
run.conf #程序执行 配置项
脚本展示
注:关键代码部分,已做解释。
1.run.sh
#!/bin/sh
### ====================================================================== ###
## ##
## Jar Stand-alone Programme Bootstrap Script ##
## ##
### ====================================== ================================ ###
# 2019-09-21 script by vincent
Usage(){
echo Usage:
echo "$0 start|start-console|stop|restart|restart-console|status"
}
# get shell path by $0(script name)
SHELLPATH=$(cd "`dirname "$0"`" >/dev/null;pwd)
# get script's name by $0
SHELLNAME=$(basename "$0")
# shell's parent dictory
AcpSrvHome=$(cd "`dirname "$SHELLPATH"`" >/dev/null;pwd)
# locate the start class
APPNAME=com.vincent.test.BoostServer
NAME=BoostServer
# load the extra file
CONF="$SHELLPATH/run.conf"
# Load run.conf
if [ -r "$CONF" ];then
. "$CONF"
else
echo "config file:$CONF error!"
exit 1
fi
#check the authority
if [ -n "$run_as" -a "$run_as" != "root" -a "`id -u`" = "0" ];then
su - $run_as -c "$SHELLPATH/$SHELLNAME $*"
exit $?
fi
# Set default parameter
[ -z "$VERBOSE" ] && VERBOSE=no
[ -z "$log_home" ] && log_home="$AcpSrvHome/logs"
[ -z "$JAVA" ] && JAVA=$JAVA_HOME/bin/java
[ -z "$PIDFILE" ] && PIDFILE="$SHELLPATH/run.pid"
[ -z "$LOGFILE" ] && LOGFILE="$log_home/$NAME.log"
[ -z "$restart_sleep" ] && restart_sleep=30
PID=
# method check environment
checkEnv(){
local chkErr
chkErr=0
# Check env and parameters
[ -z "$JAVA_HOME" -o ! -x "$JAVA" ] && echo "JAVA_HOME error,$JAVA no exist,or no permission!" && chkErr=2
touch "$PIDFILE" > /dev/null
[ "$?" != "0" -o ! -r "$PIDFILE" ] && echo "PIDFILE:$PIDFILE read/write error!" && chkErr=2
touch "$LOGFILE" > /dev/null
[ "$?" != "0" ] && echo "$LOGFILE not have write permission!" && chkErr=2
return $chkErr
}
getrunPSinfo(){
ps aux|grep "java .*\s$APPNAME" 2>/dev/null
}
getrunPID(){
ps aux|grep "java .*\s$APPNAME" 2>/dev/null|awk '{print $2}'
}
getfilePID(){
if [ -f "$PIDFILE" ];then
cat "$PIDFILE"|tail -1
fi
}
chkPID(){
local fpid pspid
fpid=$(getfilePID)
psnum=$(getrunPID|wc -l)
#TODO:发现存在多个进程时,如何处理?
pspid=$(getrunPID|tail -1)
if [ -n "$pspid" ];then
[ "$fpid" != "$pspid" ] && echo -n "$pspid" > "$PIDFILE"
echo $pspid
else
echo -n '' > "$PIDFILE"
fi
}
start(){
local type
type=$1
PID=$(chkPID)
if [ -n "$PID" ];then # already running!
echo "$NAME already running!(pid:$PID)"
return 0
fi
# Set CLASSPATH
CLASSPATH=$AcpSrvHome
export CLASSPATH=$CLASSPATH
if [ "$type" = "console" ];then
$JAVA $JAVA_OPTS $APPNAME
else
# start execute and append log‘s file to LOGFILE
nohup $JAVA $JAVA_OPTS $APPNAME >> "$LOGFILE" 2>&1 &
# start execute the jar
# nohup $JAVA -jar $JAVA_OPTS jarname >> "$LOGFILE" 2>&1 &
PID=$!
sleep 1
ckPID=$(chkPID)
if [ "$PID" = "$ckPID" ];then
echo $PID > "$PIDFILE"
echo "$NAME started(pid:$ckPID)."
else
tail -n 10 "$LOGFILE"
echo "$NAME start failed!"
fi
fi
}
stop(){
local cnt retry ckpid killsign
# return values:
# 0 = not running
# 1 = stoped
# 2 = stop failed
PID=$(chkPID)
if [ -z "$PID" ];then
echo "$NAME not running!"
return 0
fi
cnt=1
retry=10
killsign=
while [ $cnt -le $retry ];do
ckpid=$(chkPID)
if [ -n "$ckpid" ];then
[ $cnt -gt 6 ] && killsign="-9"
echo "$cnt:kill $killsign $PID"
kill $killsign $PID
sleep 1
else
echo "$NAME stoped!"
break
fi
cnt=$(expr $cnt + 1)
done
ckpid=$(chkPID)
if [ -n "$ckpid" ];then
echo "$NAME stop failed!($ckpid)" && return 2
else
[ "$killsign" = "-9" ] && return 1 # kill -9 stop
return 0 # kill stoped
fi
}
# execute with param
case "$1" in
start)
checkEnv || exit 2
start
;;
start-console)
checkEnv || exit 2
start console
;;
stop)
stop
;;
restart|restart-console)
checkEnv || exit 2
stop
retv=$?
[ "$1" = "restart-console" ] && type=console
case "$retv" in
0)
start $type
;;
1)
cnt=1
echo -n "Waiting zookeeper timeout"
#you can ignore,my project registered on the zk.
while [ $cnt -lt $restart_sleep ];do
echo -n .
sleep 1
cnt=$(expr $cnt + 1)
done
#################################################
echo
start $type
;;
*)
echo "stop failed!"
;;
esac
;;
status)
if [ "$VERBOSE" != "no" ];then
#env
echo NAME=$NAME
echo SHELLNAME=$SHELLPATH/$SHELLNAME
echo AcpSrvHome=$AcpSrvHome
echo PIDFILE=$PIDFILE
echo LOGFILE=$LOGFILE
echo
fi
PID=$(chkPID)
if [ -z "$PID" ];then
echo "$NAME is not running."
exit 1
else
[ "$VERBOSE" = "yes" ] && getrunPSinfo
echo
echo "$NAME is running.(pid:$PID)"
fi
;;
chkEnv)
checkEnv || exit 2
;;
version)
echo "$NAME bootstrap script."
;;
*)
#remind adding the param to execute shell
Usage
exit 1
;;
esac
exit 0
2.run.conf
#脚本执行用户
run_as=admin
# 日志存放路径
log_home=/usr/local/vincentlog
JMX_REMOTE_PORT=9109
DATE_STR=$(date '+%Y%m%d_%H%M%S')
# 重启动等待时间(秒),避免zookeeper被注销问题
restart_sleep=30
VERBOSE=yes
# to append the java options
JAVA_OPTS=
# Djava.ext.dirs
# JAVA_OPTS="$JAVA_OPTS -Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:$AcpSrvHome/lib"
# GC LOG
JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintCommandLineFlags -XX:HeapDumpPath=$log_home"
JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:$log_home/${NAME}.gc-${DATE_STR}.log"
# JMX REMOTE
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=$JMX_REMOTE_PORT -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
# Memorys
JAVA_OPTS="$JAVA_OPTS -Xms768m -Xmx768m -server -XX:MaxNewSize=224m -XX:NewSize=224m -XX:SurvivorRatio=5 -XX:MaxTenuringThreshold=60"
#debug
#JAVA_OPTS="$JAVA_OPTS -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8881"
脚本拆解
1.$(cd `dirname $0`;pwd)
linux获取指定文件的目录
-----------------------------------
vim /1/dirname.sh
CAD=$(cd `dirname $0`;pwd)
cd $CAD
说明:
dirname $0 :取得当前执行脚本文件的父目录
cd `dirname $0` :进入这个目录
pwd :显示当前目录(cd执行后的)
-----------------------------------
变体:
SHELLPATH=$(cd "`dirname "$0"`" >/dev/null;pwd)
AcpSrvHome=$(cd "`dirname "$SHELLPATH"`" >/dev/null;pwd)
获取当前文件路径的父路径
-----------------------------------
2.$(basename "$0") 和 $0
`basename $0`只显示当前脚本或命令的名字
$0显示会包括当前脚本或命令的路径
3.exit $?
4.文件运算符
#检测文件是否是目录
if [ -d $file ]
#检测文件是否是普通文件
if [ -f $file ]
#检测文件是否可读
if [ -r $file ]
#检测文件是否可写
if [ -w $file ]
#检测文件是否可执行
if [ -x $file ]
#检测文件是否为空
if [ -s $file ]
#检测文件(包括目录)是否存在
if [ -e $file ]
#实例
#1.如果文件可读
if [ -r $file ]
then
echo "文件可读"
else
echo "文件不可读"
fi
5.加载文件
CONF="$SHELLPATH/run.conf"
# Load run.conf
if [ -r "$CONF" ];then
#加载可读的run.conf
. "$CONF"
else
echo "config file:$CONF error!"
exit 1
fi
6.字符串监测
-n 检测字符串长度是否为0,不为0返回 true。
-z 检测字符串长度是否为0,为0返回 true。
$ 检测字符串是否为空,不为空返回 true。
= 检测两个字符串是否相等,相等返回 true。
!= 检测两个字符串是否相等,不相等返回 true。
7.逻辑运算符
-a 与运算,两个表达式都为 true 才返回 true。
-o 或运算,有一个表达式为 true 则返回 true。
! 非运算,表达式为 true 则返回 false,否则返回 true。
8.id -u (显示当前用户的uid)
##实例 -- root标识
whoami(显示当前用户的用户名)
if [ `whoami` = "root" ];then
echo "root用户!"
else
echo "非root用户!"
fi
id -u (显示当前用户的uid)
if [ `id -u` -eq 0 ];then
echo "root用户!"
else
echo "非root用户!"
fi
9.$* 和 $@
$* 和 $@ 都表示传递给函数或脚本的所有参数:
当 $* 和 $@ 不被双引号" "包围时,它们之间没有任何区别,都是将接收到的每个参数看做一份数据,彼此之间以空格来分隔。
当它们被双引号" "包含时,就会有区别了:
"$*"会将所有的参数从整体上看做一份数据,而不是把每个参数都看做一份数据。
"$@"仍然将每个参数都看作一份数据,彼此之间是独立的。
10.[ -z "$" ] &&
实例:
[[ -z ${pInputUnit} ]] && pInputUnit=$m
含义:如果变量 pInputUnit 的值为空,那么把变量m的值赋给 pInputUnit
11.[ -z "" ] && echo 0 || echo 1
参考:
https://www.bbsmax.com/A/kmzLxZ9EzG/
12.> /dev/null 输出重定向
文件描述符:
类型 | 文件描述符 | 默认情况 | 对应文件句柄位置 |
标准输入(standard input) | 0 | 从键盘获得输入 | /proc/slef/fd/0 |
标准输出(standard output) | 1 | 输出到屏幕(即控制台) | /proc/slef/fd/1 |
错误输出(error output) | 2 | 输出到屏幕(即控制台) | /proc/slef/fd/2 |
实例:
执行下面的 who 命令,它将命令的完整的输出重定向在用户文件中(users):
$ who > users
执行后,并没有在终端输出信息,这是因为输出已被从默认的标准输出设备(终端)重定向到指定的文件。
$ echo "菜鸟教程:www.runoob.com" > users
$ cat users
菜鸟教程:www.runoob.com
13. 2>&1
这条命令用到了重定向绑定,采用&可以将两个输出绑定在一起。这条命令的作用是错误输出将和标准输出同用一个文件描述符,说人话就是错误输出将会和标准输出输出到同一个地方。
linux在执行shell命令之前,就会确定好所有的输入输出位置,并且从左到右依次执行重定向的命令,所以>/dev/null 2>&1的作用就是让标准输出重定向到/dev/null中(丢弃标准输出),然后错误输出由于重用了标准输出的描述符,所以错误输出也被定向到了/dev/null中,错误输出同样也被丢弃了。执行了这条命令之后,该条shell命令将不会输出任何信息到控制台,也不会有任何信息输出到文件中。
14.touch
Linux touch命令用于修改文件或者目录的时间属性,包括存取时间和更改时间。若文件不存在,系统会建立一个新的文件。
ls -l 可以显示档案的时间记录。
$ ls -l testfile #查看文件的时间属性
#原来文件的修改时间为16:09
-rw-r--r-- 1 hdd hdd 55 2011-08-22 16:09 testfile
$ touch testfile #修改文件时间属性为当前系统时间
$ ls -l testfile #查看文件的时间属性
#修改后文件的时间属性为当前系统时间
-rw-r--r-- 1 hdd hdd 55 2011-08-22 19:53 testfile
15.nohup command &
# nohup java -jar xxxx.jar &
为了不让一些执行信息输出到前台(控制台),我们还会加上刚才提到的>/dev/null 2>&1命令来丢弃所有的输出:
# nohup java -jar xxxx.jar >/dev/null 2>&1 &
16.ps aux|grep XXX (进程监控)
ps aux输出格式:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
17.awk '{print $2}'
实例:
$2:表示第二个字段
print $2 : 打印第二个字段
awk '{print $2}' $fileName : 一行一行的读取指定的文件, 以空格作为分隔符,打印第二个字段
比如有这样一个文件
a1 b1 c1 d1
a2 b2 c2 d2
执行的结果是,输出
b1
b2
18.wc
wc [-clw][--help][--version][文件...]
-l或--lines 只显示行数。
小结
1.此脚本可用于管理jar、class程序 的启动、停止、重启等行为,使用时需匹配参数
sh run.sh start|start-console|stop|restart|restart-console|status
2.restart() 方法中,执行stop()后,sleep 30s是因我程序中有数据注册到了zk,等待失效。此部分可以删除。