万能的 Linux 应用启停脚本

🐧 万能 Linux 应用启停脚本

通用模板 + 保姆级讲解,新手也能秒会

适用场景:后端程序 / 二进制程序 / 各类服务

先聊聊痛点:你是不是也遇到过这些问题?

  • 部署程序要敲 nohup&,日志重定向每次输错
  • 重启服务找不到 PID,ps -ef | grep 一顿操作还可能杀错进程
  • 程序启动后,不确定是否成功监听端口,也不知道资源占用情况

如果你中了任意一条,这个万能启停脚本就是你的救星!

脚本核心功能一览

自动获取绝对路径,任意目录执行不报错
一键启动:赋权 + 后台运行 + PID检测
一键停止:优雅关闭,超时强制终止
状态查看:精准判断程序占用情况
资源监控:CPU/内存占用详情一目了然

完整脚本代码(可直接复制)

#!/bin/bash

# 检查并安装依赖工具
check_dependencies() {
    if ! command -v ps &> /dev/null || ! command -v awk &> /dev/null; then
        echo "Error: ps/awk command not found, basic system tools missing!"
        exit 1
    fi
}

# 先检查依赖
check_dependencies

# 获取脚本所在目录的绝对路径
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)

# ====================== 这里是【唯一需要修改】的配置区 ======================
APP_NAME="utool"                       # 应用名称,随便改,比如改成你的程序名
APP_PATH="$SCRIPT_DIR/$APP_NAME"            # 应用可执行文件路径
# ==========================================================================

# 定义PID文件路径,启动后自动写入进程PID,停止时读取该文件的PID,无需端口
PID_FILE="$SCRIPT_DIR/$APP_NAME.pid"
# 转换为规范的绝对路径(处理../ 避免路径错误)
APP_PATH=$(readlink -f "$APP_PATH")

# 从PID文件读取PID,并校验PID对应的进程是否真实运行(防止僵尸PID文件)
get_pid_by_file() {
    if [ -f "$PID_FILE" ]; then
        local pid=$(cat "$PID_FILE")
        # 校验PID是否有效:数字格式 + 进程是否存在
        if [[ "$pid" =~ ^[0-9]+$ ]] && ps -p "$pid" > /dev/null 2>&1; then
            echo "$pid"
            return 0
        fi
        # PID文件存在但进程已死,清理无效PID文件
        rm -f "$PID_FILE"
    fi
    echo ""
}

# 检查应用是否正在运行
is_running() {
    local pid=$(get_pid_by_file)
    if [ -n "$pid" ]; then
        return 0  # 正在运行 (shell中0代表true)
    fi
    return 1  # 未运行 (shell中非0代表false)
}

# 查看进程资源占用情况
resources() {
    if ! is_running; then
        echo "$APP_NAME is not running"
        return 0
    fi

    local pid=$(get_pid_by_file)
    
    echo "Resource usage for $APP_NAME (PID: $pid):"
    echo "----------------------------------------"
    
    # 使用ps命令获取详细资源占用信息
    # %cpu: CPU使用率, %mem: 内存使用率, vsz: 虚拟内存, rss: 物理内存(KB)
    ps -p "$pid" -o %cpu,%mem,vsz,rss,cmd --no-headers
    
    echo -e "\nMemory details (KB):"
    echo "----------------------------------------"
    # 显示更详细的内存使用信息
    if [ -f "/proc/$pid/statm" ]; then
        local statm=$(cat /proc/$pid/statm)
        local size=$(echo $statm | awk '{print $1}')  # 总程序大小
        local resident=$(echo $statm | awk '{print $2}')  # 驻留集大小
        local share=$(echo $statm | awk '{print $3}')  # 共享内存
        echo "Total program size: $size"
        echo "Resident set size: $resident"
        echo "Shared memory: $share"
    else
        echo "Detailed memory info not available"
    fi
}

# 启动服务 (启动后自动写入PID到文件)
start() {
    if is_running; then
        local pid=$(get_pid_by_file)
        echo "$APP_NAME is already running (PID: $pid)"
        return 0
    fi

    echo "Starting $APP_NAME ..."
    if [ -f "$APP_PATH" ]; then
        # 自动赋予执行权限,避免手动授权
       if [ ! -x "$APP_PATH" ]; then
         chmod +x "$APP_PATH"
       fi
        # nohup后台运行,日志静默输出到黑洞,不生成nohup.out文件
        nohup "$APP_PATH" > /dev/null 2>&1 &
        local pid=$!
        # 核心变更3:将启动成功的进程PID写入本地PID文件
        echo "$pid" > "$PID_FILE"
        echo "$APP_NAME started successfully (PID: $pid, PID file: $PID_FILE)"
    else
        echo "Error: $APP_PATH not found!"
        return 1
    fi
}

# 停止服务 (从PID文件读取PID执行关闭,先优雅再强制)
stop() {
    if ! is_running; then
        echo "$APP_NAME is not running"
        rm -f "$PID_FILE" # 清理残留的无效PID文件
        return 0
    fi

    local pid=$(get_pid_by_file)
    
    echo "Stopping $APP_NAME (PID: $pid)..."
    
    # 先尝试优雅关闭(kill -15),给程序释放资源的时间
    kill "$pid"
    
    # 等待进程退出,最多等10秒
    local count=0
    while is_running; do
        count=$((count + 1))
        if [ $count -gt 10 ]; then
            # 10秒后仍未退出,强制终止(kill -9)
            echo "Force stopping $APP_NAME (kill -9 $pid)..."
            kill -9 "$pid"
            break
        fi
        sleep 1
    done
    
    # 停止成功后,删除PID文件
    if ! is_running; then
        rm -f "$PID_FILE"
        echo "$APP_NAME stopped successfully, PID file deleted"
    else
        echo "Failed to stop $APP_NAME"
    fi
}

# 重启服务
restart() {
    stop
    # 无需校验端口,直接启动即可
    start
}

# 查看服务状态
status() {
    if is_running; then
        local pid=$(get_pid_by_file)
        echo "$APP_NAME is running normally (PID: $pid)"
        echo "PID file path: $PID_FILE"
    else
        if [ -f "$PID_FILE" ]; then
            echo "$APP_NAME is not running, but invalid PID file exists (cleaned automatically)"
            rm -f "$PID_FILE"
        else
            echo "$APP_NAME is not running, no PID file found"
        fi
    fi
}

# 显示帮助信息
usage() {
    echo "Usage: $0 {start|stop|restart|status|resources}"
    echo "Commands:"
    echo "  start     - Start the $APP_NAME service"
    echo "  stop      - Stop the $APP_NAME service"
    echo "  restart   - Restart the $APP_NAME service"
    echo "  status    - Check $APP_NAME running status"
    echo "  resources - Show resource usage of $APP_NAME"
    exit 1
}

# 主逻辑:接收用户传入的命令参数
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    status)
        status
        ;;
    resources)
        resources
        ;;
    *)
        usage
        ;;
esac
    

快速适配你的程序(3步搞定)

核心优势:99% 代码不用改,只改 2 行配置

步骤1:找到配置区

脚本中醒目标注的配置区域:

# ====================== 这里是【唯一需要修改】的配置区 ======================
APP_NAME="utool"                       # 应用名称
APP_PATH="$SCRIPT_DIR/$APP_NAME"    # 程序路径
# ==========================================================================

步骤2:修改配置项

配置项 说明 示例
APP_NAME 自定义应用名称 APP_NAME="myserver"
APP_PATH 程序可执行文件路径 APP_PATH="/usr/bin/myserver"

步骤3:赋予执行权限

保存脚本为 appctl.sh,执行以下命令:

chmod +x appctl.sh

脚本使用方法(命令合集)

# 启动应用
./appctl.sh start

# 停止应用
./appctl.sh stop

# 重启应用
./appctl.sh restart

# 查看状态
./appctl.sh status

# 查看资源占用
./appctl.sh resources

必学 Shell 知识点

知识点1:判断命令是否存在的黄金写法

使用 command -v 而非 which,兼容性更强:

if ! command -v lsof &> /dev/null; then

知识点2:Shell 的 true/false 反人类设定

与其他语言相反:0 = 成功/真,非0 = 失败/假

知识点3:nohup + & 后台运行终极方案

实现程序断开 SSH 后仍常驻后台:

nohup "$APP_PATH" > /dev/null 2>&1 &

知识点4:优雅关闭 vs 强制关闭

优先 kill PID(优雅释放资源),超时再用 kill -9 PID(强制终止)

知识点5:lsof 查看端口占用的神仙用法

精准查找监听指定端口的进程:

lsof -Pan -i tcp:$port -s tcp:LISTEN

总结

这个脚本是后端开发/运维的高效工具,核心优势:

  • 通用:适配所有 Linux 发行版和二进制程序
  • 省心:自动处理依赖、路径、权限等问题
  • 规范:遵循大厂启停逻辑,安全可靠
  • 易用:3 行配置即可上手,零学习成本

Linux 的精髓在于自动化脚本,用好这个模板,让你从重复操作中解放出来!