#!/bin/bash if [ $# -ne 1 ]; then echo "Usage...

बनाया गया: 10 दिसंबर 2025

का उपयोग करके उत्तर दिया गया GPT-5.1 द्वारा Chat01

प्रश्न

#!/bin/bash

if [ # -ne 1 ]; then echo "Usage: 0 <HADOOP_HOME>"
echo "Example: $0 /export/jcloud-hadoop/hadoop-2.7.3-server"
exit 1
fi

HADOOP_HOME=1RECORDFILE=1 RECORD_FILE={HADOOP_HOME}/ranger_server_test.out
MAX_MEM_THRESHOLD=5 # 内存阈值,单位GB,可修改

检查进程状态

PROCESS_STATE="DOWN"
if pgrep -f "loop_ranger_server_test.sh" > /dev/null; then
PROCESS_STATE="UP"
fi

获取昨天0点的时间戳

YESTERDAY_START=$(date -d "yesterday 00:00:00" '+%Y-%m-%d %H:%M:%S')

获取从昨天0点开始到现在的成功循环次数

LATEST_SUCCESS_LOOP=0
if [ -f "{RECORD_FILE}" ]; then # 使用awk来处理时间范围内的PASS记录 LATEST_SUCCESS_LOOP=(awk -v start_time="YESTERDAY_START" ' /PASS/ { # 提取时间戳 (格式: 2024-01-01 12:30:45) match(0, /[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/)
if (RSTART > 0) {
log_time = substr(0, RSTART, RLENGTH) if (log_time >= start_time) { count++ } } } END { print count+0 } ' {RECORD_FILE})
fi

获取当前循环次数

CURRENT_LOOP=(grep"loop:"(grep "loop:" {RECORD_FILE} 2>/dev/null | tail -1 | grep -o "loop: [0-9]*" | cut -d' ' -f2)
if [ -z "$CURRENT_LOOP" ]; then
CURRENT_LOOP=0
fi

获取yhdfs server内存使用

YHDFS_MEM=(ssh 11.241.202.184 "ps aux | grep -v grep | grep 'jd.jdufs.server.JdufsServer' | awk '{print \6}' | head -1")
if [ ! -z "YHDFSMEM"];thenYHDFSMEMGB=YHDFS_MEM" ]; then YHDFS_MEM_GB=(echo "scale=1; YHDFSMEM/1024/1024"bc)YHDFSMEMINFO="YHDFS_MEM/1024/1024" | bc) YHDFS_MEM_INFO="{YHDFS_MEM_GB}g"
# 判断内存是否超过阈值
MEM_OK=(echo"(echo "YHDFS_MEM_GB < $MAX_MEM_THRESHOLD" | bc)
else
YHDFS_MEM_INFO="N/A"
MEM_OK=0
fi

echo "loop ranger server test script process state: PROCESSSTATE,latestsuccessloop:{PROCESS_STATE}, latest success loop: {LATEST_SUCCESS_LOOP}, current loop: CURRENTLOOP,yhdfsserverRES:{CURRENT_LOOP}, yhdfs server RES: {YHDFS_MEM_INFO}"

分析一下这个代码 特别是时间的那一段代码 为什么要这么设计,你能举个案例出来么?

उत्तर

下面我会逐段解释脚本逻辑,尤其是你重点关注的处理昨天 0 点之后 PASS 次数的时间过滤那段 awk 代码,并给你一个**完整示例(包含日志内容与计算过程)**帮助理解为何这样设计。


脚本整体作用概述

这个脚本用来监控 ranger server 的循环测试情况,包括:

  • 测试脚本是否还在运行(UP/DOWN)
  • 昨天 0 点到现在的成功次数(PASS)
  • 当前 loop 数
  • yhdfs server 内存是否超过阈值

🔍 重点分析:时间过滤这一段代码

你关心的部分如下:

bash
# 获取昨天0点的时间戳 YESTERDAY_START=$(date -d "yesterday 00:00:00" '+%Y-%m-%d %H:%M:%S') # 获取从昨天0点开始到现在的成功循环次数 LATEST_SUCCESS_LOOP=$(awk -v start_time="$YESTERDAY_START" ' /PASS/ { match($0, /[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/) if (RSTART > 0) { log_time = substr($0, RSTART, RLENGTH) if (log_time >= start_time) { count++ } } } END { print count+0 } ' ${RECORD_FILE})

🎯 为什么要这么设计?

1. 需求是统计“昨天 0 点到现在”之间的 PASS 次数

也就是说:
不能统计更早的 PASS
也不能统计未来的 PASS

因此要做两个关键动作:

✔(1)取得"昨天零点"的时间字符串(同样格式)

bash
YESTERDAY_START="2024-02-10 00:00:00"

✔(2)从日志中找到 PASS 行,并提取里面的时间戳

因为日志格式类似:

text
2024-02-10 01:23:45 PASS loop: 123 2024-02-09 22:00:12 PASS loop: 100

所以必须通过正则提取时间:

awk
match($0, /YYYY-MM-DD HH:MM:SS/)

✔(3)字符比较时间字符串

关键点:
YYYY-MM-DD HH:MM:SS 这种格式可以直接字符串比较大小!

例如:

text
"2024-02-10 01:00:00" > "2024-02-10 00:00:00" → true "2024-02-09 23:59:59" < "2024-02-10 00:00:00" → true

因此这样设计是为了避免复杂的时间戳转换。


📘 举一个完整案例(最关键部分)

假设今天是 2024-02-11

📌 脚本算出:

YESTERDAY_START="2024-02-10 00:00:00"

你的 ranger_server_test.out 内容如下:

text
2024-02-09 21:00:01 PASS loop: 50 2024-02-10 01:20:10 PASS loop: 60 2024-02-10 10:15:33 PASS loop: 61 2024-02-10 23:59:59 PASS loop: 62 2024-02-11 01:00:00 PASS loop: 70

🔎 awk 如何逐行处理?

1. 读取行:

2024-02-09 21:00:01 PASS loop: 50

提取时间:2024-02-09 21:00:01
比较:

2024-02-09 21:00:01 >= 2024-02-10 00:00:00 ?  false

不计数


2.

2024-02-10 01:20:10 PASS loop: 60
2024-02-10 01:20:10 >= 2024-02-10 00:00:00 ?  true

count = 1


3.

2024-02-10 10:15:33 PASS loop: 61
>= 2024-02-10 00:00:00 → true

count = 2


4.

2024-02-10 23:59:59 PASS loop: 62

符合
count = 3


5.

2024-02-11 01:00:00 PASS loop: 70

虽然在今天,仍然 >= 昨天 0 点
符合需求(昨天 0 点到现在)

count = 4


🧮 最终结果

LATEST_SUCCESS_LOOP = 4

🎯 为什么要用 match + 字符串比较?

原因如下:

✔ 日志里 PASS 记录包含时间,但位置不固定

必须用正则匹配时间戳。

✔ 不转换为 epoch 时间可以提高效率

字符串比较即可判断时间顺序。

✔ 格式固定:YYYY-MM-DD HH:MM:SS

天然可排序,因此无需复杂操作。


🧵 如果不这样写,会有什么问题?

方法问题
grep + cut如果日志格式变动,容易提取失败
awk + 时间戳转换(mktime)依赖系统 locale,且复杂、易出错
仅用字符串匹配 PASS无法判断时间范围

当前写法 最稳健、最兼容、最不依赖环境


🙋 是否需要我给你画一个流程图说明处理流程?或者你想优化这段 awk?

प्रश्न

text
LATEST_SUCCESS_LOOP=$(awk -v start_time="$YESTERDAY_START" ' /PASS/ { # 提取时间戳 (格式: 2024-01-01 12:30:45) match($0, /[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/) if (RSTART > 0) { log_time = substr($0, RSTART, RLENGTH) if (log_time >= start_time) { count++ } } } END { print count+0 } ' ${RECORD_FILE})

我主要是这一段 不知道说什么 match awk 我不太会,最后为什么要加个 record_file?

उत्तर

这段其实就是:在日志文件 ${RECORD_FILE} 里,找出包含 PASS 的行;从每行里“抠”出时间戳;如果时间戳 ≥ 昨天 0 点,就把计数 +1;最后输出计数。
你卡住的点主要有两个:match() 是啥、最后为啥要加 ${RECORD_FILE}


1) awk ... '程序' ${RECORD_FILE} 这一坨在干嘛?

awk 的常见用法是:

bash
awk '这里写处理规则' 文件名
  • 单引号 '...' 里面写的是 awk 程序(怎么处理每一行)
  • 最后的 ${RECORD_FILE}输入数据来源(要处理哪个文件)

如果你不写 ${RECORD_FILE},awk 就不知道去读哪个文件(除非你用管道给它喂输入,比如 cat file | awk '...',或者让它从标准输入等你手动输入)。

所以这句:

bash
awk ... '...' ${RECORD_FILE}

等价于:“用 awk 逐行读取 ${RECORD_FILE},按引号里的规则处理”。


2) /PASS/ { ... } 是什么意思?

在 awk 里:

awk
/PASS/ { ... }

表示:只有当前这一行匹配正则 PASS(即行里包含 PASS)时,才执行花括号里的动作。


3) match($0, /.../) 是什么意思?

match(字符串, 正则) 用来在“字符串里找符合正则的那一段”。

  • $0:awk 的内置变量,代表 当前整行文本
  • /[0-9]{4}-.../:正则表达式,匹配类似 2024-01-01 12:30:45 的时间戳

调用 match() 以后,awk 会设置两个内置变量:

  • RSTART:匹配到的子串从第几个字符开始(从 1 开始),找不到就是 0
  • RLENGTH:匹配到的子串长度,找不到就是 -1

所以这句:

awk
match($0, /YYYY-MM-DD HH:MM:SS/)

意思是:在这一整行 $0 里找时间戳,如果找到了,就记下它的位置和长度。


4) if (RSTART > 0) { log_time = substr($0, RSTART, RLENGTH) }

  • RSTART > 0:说明找到了时间戳
  • substr($0, RSTART, RLENGTH):从整行 $0 里,把刚才 match 到的那段时间戳截出来

举例:假设某一行是

INFO ... 2024-02-10 01:20:10 ... PASS loop: 60

match 找到时间戳后,substr(...) 就会得到:

log_time = "2024-02-10 01:20:10"

5) if (log_time >= start_time) { count++ } 为啥能这么比?

因为时间格式是 YYYY-MM-DD HH:MM:SS,这个格式的字符串按字典序比较,恰好等价于时间先后比较

比如:

  • "2024-02-10 01:00:00" >= "2024-02-10 00:00:00"
  • "2024-02-09 23:59:59" >= "2024-02-10 00:00:00"

所以:只要日志里的时间戳也是这个格式,就可以直接用字符串比较筛选“昨天0点之后”的 PASS。


6) END { print count+0 } 是干嘛的?为什么 +0

END { ... }:awk 把文件所有行都处理完后,再执行一次这里面的东西。

print count+0 有两个小目的:

  1. 如果从来没加过 count++count 可能是空/未定义;+0 会把它变成 0
  2. 保证输出是数字(0、1、2…),而不是空行

7) 最后再回到你问的:“为什么要加 ${RECORD_FILE}?”

因为 awk 必须知道 读哪个文件。你这段 awk 不是从管道输入来的,所以要在最后写文件名:

bash
... 'awk程序' ${RECORD_FILE}

否则 awk 会等标准输入,脚本跑起来就会卡住(或读不到数据)。


如果你愿意,我也可以把这段 awk 改成更容易读的版本(比如直接让 awk 只处理“以时间开头的行”,或者在日志格式固定的情况下简化掉 match/substr),但不影响功能。

इस प्रश्नोत्तर को साझा करें