本文目录导读:

- 通用 Shell 脚本 (适用于 Java、Python 等后台进程)
- 针对 Java 程序 (Systemd 风格 - 推荐生产环境)
- 针对 Python 程序 (使用 Gunicorn/Uvicorn)
- 针对 Go / C / C++ 编译的二进制程序
- Windows 批处理脚本 .bat
- 总结最佳实践
这里为您整理了几种常见环境下(Java、Python、通用服务)的启动停止脚本示例,并加入了最佳实践(如PID文件、检测进程、优雅关闭)。
通用 Shell 脚本 (适用于 Java、Python 等后台进程)
这是最经典的模板,使用 PID 文件来管理进程。
#!/bin/bash
# description: 通用服务启动/停止脚本
# usage: ./service.sh {start|stop|restart|status}
# 请修改以下变量为您的服务名和启动命令
APP_NAME="my_app"
APP_HOME="/path/to/your/app"
EXECUTABLE="java -jar $APP_HOME/app.jar"
PID_FILE="$APP_HOME/$APP_NAME.pid"
LOG_FILE="$APP_HOME/$APP_NAME.log"
# 获取当前进程 PID
get_pid() {
if [ -f "$PID_FILE" ]; then
cat "$PID_FILE"
else
echo ""
fi
}
# 检查进程是否在运行
is_running() {
local pid=$(get_pid)
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
return 0 # true
else
return 1 # false
fi
}
# 启动服务
start() {
if is_running; then
echo "$APP_NAME 已经在运行中 (PID: $(get_pid))"
exit 1
fi
echo "正在启动 $APP_NAME ..."
# 使用 nohup 在后台运行,并输出日志
nohup $EXECUTABLE > "$LOG_FILE" 2>&1 &
local new_pid=$!
echo $new_pid > "$PID_FILE"
sleep 1
if is_running; then
echo "$APP_NAME 启动成功 (PID: $new_pid)"
else
echo "$APP_NAME 启动失败,请检查日志: $LOG_FILE"
exit 1
fi
}
# 停止服务
stop() {
if ! is_running; then
echo "$APP_NAME 未在运行"
return 0
fi
local pid=$(get_pid)
echo "正在停止 $APP_NAME (PID: $pid) ..."
# 1. 尝试优雅关闭 (SIGTERM)
kill "$pid" 2>/dev/null
# 2. 等待进程结束(最多等待 30 秒)
local wait_count=0
while is_running && [ $wait_count -lt 30 ]; do
sleep 1
((wait_count++))
done
# 3. 如果还在运行,强制杀掉 (SIGKILL)
if is_running; then
echo "进程未能优雅停止,强制执行 kill -9 ..."
kill -9 "$pid" 2>/dev/null
sleep 1
fi
# 4. 删除 PID 文件
rm -f "$PID_FILE"
echo "$APP_NAME 已停止"
}
# 重启服务
restart() {
stop
sleep 2 # 等待端口释放
start
}
# 查看状态
status() {
if is_running; then
echo "$APP_NAME 正在运行 (PID: $(get_pid))"
else
echo "$APP_NAME 未在运行"
fi
}
# 主入口
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
status)
status
;;
*)
echo "用法: $0 {start|stop|restart|status}"
exit 1
;;
esac
针对 Java 程序 (Systemd 风格 - 推荐生产环境)
对于 CentOS 7+ / Ubuntu 16+,使用 systemd 是最现代化的方式。
文件路径: /etc/systemd/system/myapp.service
[Unit] Description=My Java Application Service After=network.target [Service] Type=simple User=appuser WorkingDirectory=/opt/myapp ExecStart=/usr/bin/java -Xms256m -Xmx1024m -jar /opt/myapp/app.jar ExecStop=/bin/kill -s TERM $MAINPID SuccessExitStatus=143 Restart=always RestartSec=10 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target
使用方法:
# 重新加载配置 sudo systemctl daemon-reload # 启动 sudo systemctl start myapp # 停止 sudo systemctl stop myapp # 重启 sudo systemctl restart myapp # 查看状态 sudo systemctl status myapp # 设置为开机自启 sudo systemctl enable myapp
针对 Python 程序 (使用 Gunicorn/Uvicorn)
对于 Web 应用 (Flask/Django/FastAPI) 经常配合 Gunicorn。
文件路径: /etc/init.d/my-python-app 或作为 systemd 服务。
#!/bin/bash
# chkconfig: 2345 80 20
# description: Python Web Service
APP_NAME="webapp"
APP_PATH="/opt/myapp"
VENV_PATH="$APP_PATH/venv/bin/activate"
GUNICORN="$APP_PATH/venv/bin/gunicorn"
PID_FILE="/var/run/$APP_NAME.pid"
start() {
echo "Starting $APP_NAME ..."
cd $APP_PATH
source $VENV_PATH
$GUNICORN --workers 3 --bind 0.0.0.0:8000 wsgi:app \
--pid $PID_FILE \
--daemon \
--access-logfile /var/log/$APP_NAME/access.log \
--error-logfile /var/log/$APP_NAME/error.log
echo "$APP_NAME started."
}
stop() {
echo "Stopping $APP_NAME ..."
if [ -f $PID_FILE ]; then
kill -TERM $(cat $PID_FILE) 2>/dev/null
rm -f $PID_FILE
fi
echo "$APP_NAME stopped."
}
case "$1" in
start) start ;;
stop) stop ;;
restart) stop; sleep 2; start ;;
*) echo "Usage: $0 {start|stop|restart}" ;;
esac
针对 Go / C / C++ 编译的二进制程序
Go 或 C 程序通常是单文件二进制,启动简单。
#!/bin/bash
APP="/usr/local/bin/my-server"
APP_NAME="myserver"
PID_FILE="/var/run/${APP_NAME}.pid"
start() {
if [ -f $PID_FILE ] && kill -0 $(cat $PID_FILE) 2>/dev/null; then
echo "$APP_NAME already running."
exit 1
fi
echo "Starting $APP_NAME..."
$APP &>/dev/null &
echo $! > $PID_FILE
echo "$APP_NAME started."
}
stop() {
if [ ! -f $PID_FILE ]; then
echo "$APP_NAME not running."
exit 1
fi
echo "Stopping $APP_NAME..."
kill $(cat $PID_FILE) 2>/dev/null
rm -f $PID_FILE
echo "$APP_NAME stopped."
}
case "$1" in
start|stop|restart) $1 ;;
*) echo "Usage: $0 {start|stop|restart}" ;;
esac
Windows 批处理脚本 .bat
@echo off
set APP_NAME=MyApp
set JAR_PATH=C:\myapp\app.jar
set PID_FILE=%TEMP%\%APP_NAME%.pid
if "%1"=="start" goto start
if "%1"=="stop" goto stop
if "%1"=="restart" goto restart
echo Usage: %0 {start^|stop^|restart}
goto :eof
:start
echo Starting %APP_NAME% ...
start "%~n0" javaw -jar %JAR_PATH%
echo %ERRORLEVEL% > %PID_FILE%
echo %APP_NAME% started.
goto :eof
:stop
echo Stopping %APP_NAME% ...
taskkill /f /fi "PID eq %PID_FILE%" 2>nul
del %PID_FILE% 2>nul
echo %APP_NAME% stopped.
goto :eof
:restart
call :stop
timeout /t 2 /nobreak >nul
call :start
goto :eof
总结最佳实践
| 特性 | 说明 |
|---|---|
| PID 文件 | 用于存储进程ID,方便查找和杀死 |
| SIGTERM vs SIGKILL | 先发 TERM (15) 优雅关闭,等几秒再发 KILL (9) 强制 |
| 日志重定向 | nohup + 2>&1 将输出写入文件 |
| 状态检查 | kill -0 PID 检查进程是否存在 |
| Systemd | 生产环境推荐,自带PID、自动重启、资源限制 |
您可以根据自己的服务类型选择对应的脚本模板,如果您有具体的服务(比如是 Spring Boot、Flask、Node.js),我可以帮您定制更精确的版本。