练习编写一个MineCreaft 系统服务启动脚本,这样我不仅仅可以方便的使用service启动MC服务器,又可以练习shell编程,一举两得

MineCraft 服务器启动脚本

大致思路

一般启动服务器的时候,我们通过screen来启动mc服务器,这样有一个好处是,有了screen开创的窗口,我们随时可以进入它去控制服务器,那么我希望 启动脚本,启动的时候 创建一个screen ,在这里面开启服务器。控制关闭等其他操作,向刚才的创建好的screen输出命令即可。

编写的时候我遇到了一些不大不小的问题,如下

  1. service 编写的规则
  2. 如何使用screen 启动mc服务器
  3. 如何screen 后台键入命令
  4. 如何判断服务是否启动

好在MineCraft WIKI百科上有启动脚本案例,其实以及完美的解决我的需求!ahh,既然如此,我就学习wiki百科上的案例!

参考链接:

  1. WIKI 百科案例
  2. screen后台键入命令

service 服务shell脚本案例

Service 日常使用规则如下:可通过 service [serviceNAME] start|stop|status|restart 命令对服务进行控制 , 这里有一个 httpd(apache),启动脚本的案例,我们可以研究一下:

#!bin/bash
#chkconfig:2345 55 25    //运行级别、启动优先级、关闭优先级
#processname:httpd        //进程名
#description:source httpd server daemon  //服务描述
prog=/usr/local/httpd/bin/apachectl      //控制程序路径
lock=/usr/local/httpd/httpd.lock        //lock文件路径
start(){                                //start函数
        $prog start
        echo "正在启动服务...."
        touch $lock
}
stop(){                                //stop函数
        $prog stop
        echo "正在停止服务...."
        rm -rf $lock
}
status(){                        //status函数
        if [ -e $lock ];then
            echo "$0 服务正在运行"
        else
            echo "$0 服务已经停止"
        fi
}
restart(){              //restart函数
        stop
        start          //直接调用stop、start函数,
}
case "$1" in        //case分支结构匹配,$1位置参数对控制参数调用
"start")
        start      //调用start函数
        ;;
"stop")            //调用stop函数
        stop
        ;;
"status")            //调用status函数
        status
        ;;
"restart")            //调用restart函数
        restart
        ;;
*)                //其他参数就输出脚本正确用法
        echo "用法:$0 start|stop|status|restart"
        ;;
esac

如果你掌握了不错的shell基础,相信这些代码都难不倒你,编写service 服务shell脚本,其实就是使用 case语句进行控制,当然为了代码的整洁,通常将一个功能封装成一个函数 ,case根据不同的 $1 (位置参数)

如何判断服务是否启动

判断服务是否启动,我们使用 pgrep命令 ,通过pgrep来获得正在被调度的进程的相关信息。pgrep通过匹配其程序名,找到匹配的进程。我们启动minecratf 服务器不希望使用root用户,这不是一个很安全的方式,所以我们可以封装一个函数,执行命令的时候 使用没有那么大权限的用户,为此,可以封装一个函数 as_user()

ME=`whoami`
#确保是 设定用户执行脚本
as_user()
{
    if [ $ME = $USERNAME ]
    then
        bash -c "$1"
    else
        su - "$USERNAME" -c "$1"
    fi
}

需要执行的命令使用 $1传入。说回 pgrep使用 ,这里用法为 pgrep -u [username] -f [process] 这样会返回 进程pid ,感觉集成了 ps | grep 两种命令的组合命令。

if pgrep -u $USERNAME -f $SERVICE > /dev/null ; then
    # 服务以及启动
    else
    # 服务没有启动
fi

> /dev/null 就是为了不让改命令的输出 出现在屏幕上

screen的使用

后台执行命令

screen后台执行命令的案例如下 screen -dmS test bash -c 'ping www.baidu.com'; ,使用 -dmS参数开启一个已断开的会话,可以绕过手动断开操作. 你想后台调用的命令 可以直接使用 bash -c 执行,当然如果执行的命令中有参数的话,需要使用 eval将命令扫描两次再去执行,第一次将变量值提取出来,第二次 执行命令。

后台键入命令

使用语法格式为 screen -S $screen_name -p 0 -X stuff "$cmd" ,原理应当是发送命令至指定会话中执行,所以前期创建会话时,应指定会话名。参数 -S 指定会话 参数 -X 发送命令 参数 stuff ?疑似?是输出命令执行 参数 -p 指定屏幕。

到这里,基本难题都解决了。 当然我们给mc后台输入命令,还需要 多输入一个 \r 才可以执行哦

code

其实就是 官方案例的摘抄,学习别人的代码也是一种进步(编程就是 ctrl+c ctrl+v)

#! /bin/bash
 # /etc/init.d/minecraft

 # info start
 # Minecraft 服务器启动脚本
 # 作者: 越行勤
 # 版本 0.1
 # 环境需要 CentsOs8 需要安装 screen tar java 等必要工具和环境
 # info end

 #Settings
SERVICE="spigot-1.17.jar"
SERVICENAME="MineCraft_spigot_1.17_SERVICE"
USERNAME="minecraft"
MCPATH="/home/minecraft/minecraft1.17"
BACKUPPATH="/home/minecraft/backup"
SCREENNAME=mc
MAXHEAP=2048
MINHEAP=1024
INVOCATION="java -Xms${MINHEAP}m -Xmx${MAXHEAP}m -jar $SERVICE nogui"

ME=`whoami`
#确保是 设定用户执行脚本
as_user()
{
    if [ $ME = $USERNAME ]
    then
        bash -c "$1"
    else
        su - "$USERNAME" -c "$1"
    fi
}
# 启动mc
mc_start()
{
    if pgrep -u $USERNAME -f $SERVICE > /dev/null ; then
        echo "$SERVICENAME is already running!"
    else
        echo "Strating $SERVICENAME ..."
        cd $MCPATH
        # 登录USRNAME用户 创建screen 启动服务器
        as_user "cd $MCPATH && screen -h $HISTORY -dmS ${SCREENNAME} bash -c '${INVOCATION}'"
        sleep 7
        # 7s后如果mc服务器还没启动 那么启动失败
        pgrep -u $USERNAME -f $SERVICE
        if [ $? = 0 ]
        then
            echo "$SERVICE is now running."
        else
            echo "Error! Could not start $SERVICE!"
        fi
    fi
}


mc_saveoff()
{
    if pgrep -u $USERNAME -f $SERVICE > /dev/null ; then
        echo "$SERVICENAME is running ... suspending saves"
        as_user "screen -S ${SCREENNAME} -p 0 -X eval 'stuff \"say SERVER BACKUP STARTING. Server going readonly...\"\r'"
        as_user "screen -S ${SCREENNAME} -p 0 -X eval 'stuff \"save-off\"\r'"
        as_user "screen -S ${SCREENNAME} -p 0 -X eval 'stuff \"save-all\"\r'"
        sync
        sleep 10
    else
        echo "$SERVICE is not running. Not suspending saves."
    fi
}


mc_saveon()
{
    if pgrep -u $USERNAME -f $SERVICE > /dev/null ; then
        echo "$SERVICENAME is running ... re-enable saves"
        as_user "screen -S ${SCREENNAME} -p 0 -X eval 'stuff \"save-on\"\r'"
        as_user "screen -S ${SCREENNAME} -p 0 -X eval 'stuff \"say Server BackUp Ended! Server going read-write...\"\r'"
        sleep 10
    else
        echo "$SERVICE is not running. Not suspending saves."
    fi
}


mc_stop()
{
    if pgrep -u $USERNAME -f $SERVICE > /dev/null ; then
        echo "stoping $SERVICENAME "
        as_user "screen -S ${SCREENNAME} -p 0 -X eval 'stuff \"say SERVER SHUTDOWN IN ten SECONDS . SAVING MAP\"\r'"
        as_user "screen -S ${SCREENNAME} -p 0 -X eval 'stuff \"save-all\"\r'"
        for i in {10..1}
        do
            as_user "screen -S ${SCREENNAME} -p 0 -X eval 'stuff \"say $i s..\"\r'"
            sleep 1
        done
        as_user "screen -S ${SCREENNAME} -p 0 -X eval 'stuff \"stop\"\r'"
        sleep 7
    else
        echo "$SERVICE was not running."
    fi
    if pgrep -u $USERNAME -f $SERVICE > /dev/null
    then
        echo "Error! $SERVICE could not be stopped."
    else
        echo "$SERVICE is stopped."
    fi
}

mc_backup()
{
# 根据需求更改
}

#Start-Stop here
case "$1" in
    start)
        mc_start
        ;;
    stop)
        mc_stop
        ;;
    restart)
        mc_stop
        mc_start
        ;;
    status)
        if pgrep -u $USERNAME -f $SERVICE > /dev/null
        then
            echo "$SERVICE is running."
        else
            echo "$SERVICE is not running."
        fi
        ;;
    *)
        echo "Usage: $0 {start|stop|update|backup|status|restart|command \"server command\"}"
        exit 1
        ;;
esac

exit 0

努力成长的程序员