241 lines
5.7 KiB
Bash
Executable File
241 lines
5.7 KiB
Bash
Executable File
#!/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 "$@"
|