feat: 添加进程监控脚本 proc_monitor.sh

- 监控指定进程名或 PID
- 每 10 秒检测一次(可配置)
- 进程消失时执行指定命令
- 支持日志记录和后台守护模式
This commit is contained in:
2026-03-26 17:13:11 +08:00
parent 39adad4cd4
commit ebfed5d168
2 changed files with 241 additions and 1 deletions

View File

@@ -28,7 +28,7 @@ tools/
| 名称 | 路径 | 说明 |
|------|------|------|
| - | - | 待添加 |
| proc_monitor.sh | scripts/proc_monitor.sh | 进程监控脚本,进程消失时执行指定命令 |
---

240
scripts/proc_monitor.sh Executable file
View File

@@ -0,0 +1,240 @@
#!/bin/bash
# =============================================================================
# 进程监控脚本
# 监控指定进程,当进程消失时执行指定命令
#
# 用法:
# ./proc_monitor.sh -p <进程名> -c "<命令>" [-i 10] [-l /path/to/log]
# ./proc_monitor.sh --pid <PID> -c "<命令>" [-i 10] [-l /path/to/log]
#
# 参数:
# -p, --process 要监控的进程名
# --pid 要监控的进程 PID
# -c, --command 进程消失后要执行的命令(必填)
# -i, --interval 检测间隔(秒,默认 10
# -l, --log 日志文件路径(可选)
# -d, --daemon 后台运行模式
# -h, --help 显示帮助
#
# 示例:
# ./proc_monitor.sh -p "myapp" -c "systemctl restart myapp" -i 10
# ./proc_monitor.sh --pid 1234 -c "echo 'Process died' >> /tmp/alert.log" -d
# =============================================================================
set -euo pipefail
# 默认配置
INTERVAL=10
LOG_FILE=""
DAEMON_MODE=false
PROCESS_NAME=""
TARGET_PID=""
EXEC_COMMAND=""
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 日志函数
log() {
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
echo -e "$msg"
if [[ -n "$LOG_FILE" ]]; then
echo -e "$msg" >> "$LOG_FILE"
fi
}
log_info() {
log "${GREEN}[INFO]${NC} $1"
}
log_warn() {
log "${YELLOW}[WARN]${NC} $1"
}
log_error() {
log "${RED}[ERROR]${NC} $1"
}
# 显示帮助
show_help() {
sed -n '/^# ==/,/^# ==/p' "$0" | sed 's/^# //' | sed 's/^#//'
}
# 解析参数
parse_args() {
while [[ $# -gt 0 ]]; do
case $1 in
-p|--process)
PROCESS_NAME="$2"
shift 2
;;
--pid)
TARGET_PID="$2"
shift 2
;;
-c|--command)
EXEC_COMMAND="$2"
shift 2
;;
-i|--interval)
INTERVAL="$2"
shift 2
;;
-l|--log)
LOG_FILE="$2"
shift 2
;;
-d|--daemon)
DAEMON_MODE=true
shift
;;
-h|--help)
show_help
exit 0
;;
*)
log_error "未知参数: $1"
show_help
exit 1
;;
esac
done
}
# 验证参数
validate_args() {
if [[ -z "$PROCESS_NAME" && -z "$TARGET_PID" ]]; then
log_error "必须指定进程名 (-p) 或 PID (--pid)"
exit 1
fi
if [[ -n "$PROCESS_NAME" && -n "$TARGET_PID" ]]; then
log_error "不能同时指定进程名和 PID请二选一"
exit 1
fi
if [[ -z "$EXEC_COMMAND" ]]; then
log_error "必须指定要执行的命令 (-c)"
exit 1
fi
if ! [[ "$INTERVAL" =~ ^[0-9]+$ ]] || [[ "$INTERVAL" -lt 1 ]]; then
log_error "检测间隔必须是正整数(秒)"
exit 1
fi
}
# 检查进程是否存在
check_process() {
if [[ -n "$TARGET_PID" ]]; then
# 通过 PID 检查
kill -0 "$TARGET_PID" 2>/dev/null
else
# 通过进程名检查
pgrep -x "$PROCESS_NAME" >/dev/null 2>&1 || \
pgrep -f "$PROCESS_NAME" >/dev/null 2>&1
fi
}
# 获取进程信息
get_process_info() {
if [[ -n "$TARGET_PID" ]]; then
echo "PID: $TARGET_PID"
else
local pid
pid=$(pgrep -x "$PROCESS_NAME" 2>/dev/null | head -1 || \
pgrep -f "$PROCESS_NAME" 2>/dev/null | head -1 || echo "unknown")
echo "进程名: $PROCESS_NAME (PID: $pid)"
fi
}
# 监控循环
monitor() {
log_info "开始监控 $(get_process_info)"
log_info "检测间隔: ${INTERVAL}"
log_info "触发命令: $EXEC_COMMAND"
log_info "按 Ctrl+C 停止监控"
echo "---"
local check_count=0
local start_time
start_time=$(date +%s)
while true; do
check_count=$((check_count + 1))
if ! check_process; then
log_warn "检测到进程已消失!"
log_info "执行命令: $EXEC_COMMAND"
# 执行命令并记录结果
if eval "$EXEC_COMMAND"; then
log_info "命令执行成功"
else
log_error "命令执行失败,退出码: $?"
fi
# 统计信息
local end_time
end_time=$(date +%s)
local duration=$((end_time - start_time))
log_info "监控时长: ${duration}秒,共检测 ${check_count}"
break
fi
# 每 6 次检测(约 1 分钟)输出一次存活确认
if [[ $((check_count % 6)) -eq 0 ]]; then
log_info "进程正常运行中... (已检测 ${check_count} 次)"
fi
sleep "$INTERVAL"
done
}
# 后台运行模式
run_daemon() {
local script_path
script_path=$(realpath "$0")
local args=""
# 重新构建参数(去掉 -d
if [[ -n "$PROCESS_NAME" ]]; then
args="-p '$PROCESS_NAME'"
elif [[ -n "$TARGET_PID" ]]; then
args="--pid '$TARGET_PID'"
fi
args="$args -c '$EXEC_COMMAND' -i $INTERVAL"
[[ -n "$LOG_FILE" ]] && args="$args -l '$LOG_FILE'"
log_info "以守护模式启动..."
log_info "日志将输出到: ${LOG_FILE:-stdout}"
# 使用 nohup 后台运行
eval "nohup bash $script_path $args >/dev/null 2>&1 &"
local pid=$!
log_info "守护进程 PID: $pid"
}
# 主函数
main() {
parse_args "$@"
validate_args
if [[ "$DAEMON_MODE" == true ]]; then
run_daemon
else
monitor
fi
}
# 捕获信号
trap 'log_info "监控已停止"; exit 0' INT TERM
# 运行主函数
main "$@"