最近要下的专辑(5.22~XXX)

1.Dave Matthews

2.莫文蔚的全集

考虑可再生能源出力不确定性的商业园区用户需求响应策略(Matlab代码实现)内容概要:本文围绕“考虑可再生能源出力不确定性的商业园区用户需求响应策略”展开,结合Matlab代码实现,研究在可再生能源(如风电、光伏)出力具有不确定性的背景下,商业园区如何制定有效的需求响应策略以优化能源调度和提升系统经济性。文中可能涉及不确定性建模(如场景生成与缩减)、优化模型构建(如随机规划、鲁棒优化)以及需求响应机制设计(如价格型、激励型),并通过Matlab仿真验证所提策略的有效性。此外,文档还列举了大量相关的电力系统、综合能源系统优化调度案例与代码资源,涵盖微电网调度、储能配置、负荷预测等多个方向,形成一个完整的科研支持体系。; 适合人群:具备一定电力系统、优化理论和Matlab编程基础的研究生、科研人员及从事能源系统规划与运行的工程技术人员。; 使用场景及目标:①学习如何建模可再生能源的不确定性并应用于需求响应优化;②掌握使用Matlab进行商业园区能源系统仿真与优化调度的方法;③复现论文结果或开展相关课题研究,提升科研效率与创新能力。; 阅读建议:建议结合文中提供的Matlab代码实例,逐步理解模型构建与求解过程,重点关注不确定性处理方法与需求响应机制的设计逻辑,同时可参考文档中列出的其他资源进行扩展学习与交叉验证。
#!/bin/bash #================================================== # 🔧 通用磁盘性能测试脚本(增强版 | 带预热 + TXT报告 | 原始值提取) # # 🚀 使用方法: # ./disk_test.sh sda rw=read --time=300 # ./disk_test.sh nvme0n1 rw=randwrite runtime=600 # ./disk_test.sh -nowarmup sda rw=randread # 跳过预热 # ./disk_test.sh sda runtime=300 rw=randread bs=4k size=100% iodepth=64 ioengine=libaio numjobs=8 # # ✅ 特性: # - 不做任何单位换算,直接提取原始 IOPS/BW/延迟 字符串 # - 支持 /dev/sda, /dev/nvme0n1 等所有块设备 # - 所有结果保存至以时间戳命名的独立子目录 # - 自动提取第6行 IOPS/BW,第9行 avg 延迟(保持原格式) # - 纯 Bash + awk/sed 解析文本,不依赖 bc/jq #================================================== # 默认参数 DEFAULT_BS="4k" DEFAULT_IODEPTH=8 DEFAULT_NUMJOBS=1 DEFAULT_RW="read" DEFAULT_RUNTIME=60 DEFAULT_RAMP_TIME=5 DEFAULT_IOENGINE="libaio" DEFAULT_DIRECT=1 DEFAULT_SIZE="100%" # 输出根目录 OUTPUT_DIR="./fio_results" mkdir -p "$OUTPUT_DIR" # 全局 CSV 表头 CSV_FILE="$OUTPUT_DIR/fio_summary.csv" if [ ! -f "$CSV_FILE" ]; then echo "timestamp,test_type,devices,rw,bs,iodepth,numjobs,size,ioengine,runtime_sec,iops_str,bw_str,latency_str" > "$CSV_FILE" fi #-------------------------------------------------- # 显示帮助信息 show_help() { awk '/^#.*🚀.*使用方法/,/^#.*💡.*提示/' "$0" | sed 's/^# //' exit 0 } for arg in "$@"; do [[ "$arg" == "-h" || "$arg" == "--help" ]] && show_help; done #-------------------------------------------------- # 参数解析 #-------------------------------------------------- WARMUP_MODE="" DEVICE_LIST_STR="" SIZE="$DEFAULT_SIZE" IOENGINE="$DEFAULT_IOENGINE" RUNTIME="$DEFAULT_RUNTIME" RAMP_TIME="$DEFAULT_RAMP_TIME" DIRECT="$DEFAULT_DIRECT" CUSTOM_JOBNAME="" BS="" NUMJOBS="" IODEPTH="" RW="" while [ $# -gt 0 ]; do arg="$1"; shift case "$arg" in -warmup) WARMUP_MODE="force" ;; -nowarmup) WARMUP_MODE="skip" ;; bs=*) BS="${arg#*=}" ;; iodepth=*) IODEPTH="${arg#*=}" ;; numjobs=*) NUMJOBS="${arg#*=}" ;; rw=*) RW="${arg#*=}" ;; runtime=*) RUNTIME="${arg#*=}" ;; --time=*|time=*) RUNTIME="${arg#*=}" ;; ramp_time=*) RAMP_TIME="${arg#*=}" ;; --ioengine=*|ioengine=*) IOENGINE="${arg#*=}" ;; direct=*) DIRECT="${arg#*=}" ;; jobname=*) CUSTOM_JOBNAME="${arg#*=}" ;; size=*) SIZE="${arg#*=}" ;; *) if [ -z "$DEVICE_LIST_STR" ]; then DEVICE_LIST_STR="$arg" else echo "⚠️ 忽略未知参数: $arg" fi ;; esac done # 设置默认值 BS="${BS:-$DEFAULT_BS}" IODEPTH="${IODEPTH:-$DEFAULT_IODEPTH}" NUMJOBS="${NUMJOBS:-$DEFAULT_NUMJOBS}" RW="${RW:-$DEFAULT_RW}" RUNTIME="${RUNTIME:-$DEFAULT_RUNTIME}" RAMP_TIME="${RAMP_TIME:-$DEFAULT_RAMP_TIME}" IOENGINE="${IOENGINE:-$DEFAULT_IOENGINE}" DIRECT="${DIRECT:-$DEFAULT_DIRECT}" SIZE="${SIZE:-$DEFAULT_SIZE}" # 检查设备 if [ -z "$DEVICE_LIST_STR" ]; then echo "❌ 错误:未指定任何设备!" show_help fi is_valid_device_short() { [[ "$1" =~ ^sd[a-z]+$ ]] || [[ "$1" =~ ^nvme[0-9]+n[0-9]+$ ]] } IFS=',' read -ra DEVICES_INPUT <<< "$DEVICE_LIST_STR" DEVICE_ARRAY=() for dev in "${DEVICES_INPUT[@]}"; do dev=$(echo "$dev" | xargs) full_dev="${dev#/dev/}"; full_dev="/dev/$full_dev" [[ "$dev" == /dev/* ]] && full_dev="$dev" if ! is_valid_device_short "$(basename "$full_dev")" && [[ ! -b "$full_dev" ]]; then echo "❌ 设备无效或不存在: $full_dev" exit 1 fi DEVICE_ARRAY+=("$full_dev") done # 检查 fio if ! command -v fio &> /dev/null; then echo "❌ 错误:fio 未安装,请运行 sudo apt install fio" exit 1 fi # 创建运行目录 TIMESTAMP_FULL="$(date '+%Y%m%d_%H%M%S')" DATE_STAMP="$(date '+%Y-%m-%d %H:%M:%S')" RUN_DIR="$OUTPUT_DIR/$TIMESTAMP_FULL" mkdir -p "$RUN_DIR" JOBNAME="${CUSTOM_JOBNAME:-${RW}_bs${BS}_iod${IODEPTH}_nj${NUMJOBS}_sz${SIZE}_eng_${IOENGINE##*/}_t${RUNTIME}s_${TIMESTAMP_FULL}}" TXT_OUT="$RUN_DIR/${JOBNAME}.txt" LOG_FILE="$RUN_DIR/summary.log" ERROR_LOG="$RUN_DIR/error.log" > "$LOG_FILE" [[ ! -f "$ERROR_LOG" ]] && echo "# FIO Error Log - $DATE_STAMP" > "$ERROR_LOG" TEST_TYPE="single_disk" (( ${#DEVICE_ARRAY[@]} > 1 )) && TEST_TYPE="concurrent_multi_disk" # 打印配置摘要 echo "🔧 主测试配置" printf " %-12s : %s\n" "Devices" "$(IFS=','; echo "${DEVICE_ARRAY[*]}")" printf " %-12s : %s\n" "RW Mode" "$RW" printf " %-12s : %s\n" "Block Size" "$BS" printf " %-12s : %s\n" "I/O Depth" "$IODEPTH" printf " %-12s : %s\n" "Num Jobs" "$NUMJOBS" printf " %-12s : %ss\n" "Runtime" "$RUNTIME" printf " %-12s : %s\n" "Test Range" "$SIZE" printf " %-12s : %s\n" "I/O Engine" "$IOENGINE" printf " %-12s : %s\n" "Test Type" "$TEST_TYPE" #-------------------------------------------------- # 预热逻辑 #-------------------------------------------------- prompt_warmup() { if [[ "$WARMUP_MODE" == "skip" ]]; then return 1; fi if [[ "$WARMUP_MODE" == "force" ]]; then return 0; fi echo "" echo "🔥 是否预热?" echo " 1) 是(推荐)" echo " 2) 否" while true; do read -p "选择 [1/2] > " ch case "$ch" in 1) return 0 ;; 2) return 1 ;; esac done } run_warmup() { local err="/tmp/fio_warmup_err_$$.log" > "$err" echo "🔄 预热中..." fio --name=warmup --rw=mixrandrw --rwmixread=70 --bs=4k --iodepth=16 --direct=1 \ --ioengine="$IOENGINE" --size="$SIZE" --output-format=normal \ $(printf -- '--filename=%s ' "${DEVICE_ARRAY[@]}") \ $( (( ${#DEVICE_ARRAY[@]} > 1 )) && echo "--group_reporting" ) \ > /dev/null 2>"$err" || echo "⚠️ 预热失败(详情见日志)" rm -f "$err" } prompt_warmup && run_warmup #-------------------------------------------------- # 执行主测试 #-------------------------------------------------- ERROR_CAPTURE="/tmp/fio_err_$$.log" > "$ERROR_CAPTURE" FIO_CMD=( fio --rw="$RW" --bs="$BS" --direct="$DIRECT" --ioengine="$IOENGINE" --iodepth="$IODEPTH" --numjobs="$NUMJOBS" --runtime="$RUNTIME" --ramp_time="$RAMP_TIME" --size="$SIZE" --time_based --output-format=normal --output="$TXT_OUT" ) for dev in "${DEVICE_ARRAY[@]}"; do FIO_CMD+=(--filename="$dev") done if (( ${#DEVICE_ARRAY[@]} > 1 )); then FIO_CMD+=(--group_reporting --name=multi_device_test) else FIO_CMD+=(--name="$JOBNAME") fi echo "" echo "🚀 正在运行主测试: $JOBNAME ..." echo "⏱️ 开始时间: $DATE_STAMP" if "${FIO_CMD[@]}" > /dev/null 2>"$ERROR_CAPTURE"; then echo "✅ 主测试完成" else echo "❌ fio 测试失败!返回码: $?" { echo "=======================================" echo "Timestamp: $DATE_STAMP" echo "Test Name: $JOBNAME" echo "Command: ${FIO_CMD[*]}" echo "--- Error Output ---" cat "$ERROR_CAPTURE" echo "" } >> "$ERROR_LOG" echo "📝 错误已记录到: $ERROR_LOG" rm -f "$ERROR_CAPTURE" exit 1 fi rm -f "$ERROR_CAPTURE" #-------------------------------------------------- # ✅ 统一提取函数:所有字段使用相同高可靠正则 #-------------------------------------------------- parse_fio_result_stable() { local file="$1" # 查找包含吞吐信息的行(读/写行 或 group_report) local throughput_line throughput_line=$(grep -i "\(iops\|bw\)" "$file" | grep -E "(^[[:space:]]*[rw].*:)|(group_report)" | head -1) # ✅ 统一使用 grep -oiE 提取字段(不区分大小写,精确匹配到分隔符前) local iops_raw=$(echo "$throughput_line" | grep -oiE "iops=[^,]*[^,[:space:]]" | cut -d= -f2 | xargs || echo "N/A") local bw_raw=$(echo "$throughput_line" | grep -oiE "bw=[^,]*[^,[:space:]]B.s" | cut -d= -f2 | xargs || echo "N/A") # 查找延迟 avg 行 local lat_line=$(grep -i "\(lat.*avg\|clat.*avg\)" "$file" | head -1) local lat_raw=$(echo "$lat_line" | grep -oiE "avg=[0-9.]*[^,[:space:]]*(ns|us|ms)" | cut -d= -f2 | xargs || echo "N/A") # 返回三者 echo "$iops_raw,$bw_raw,$lat_raw" } # 执行提取 echo "" RESULT=$(parse_fio_result_stable "$TXT_OUT") if [ -z "$RESULT" ] || ! echo "$RESULT" | grep -q ","; then echo "⚠️ 提取失败,使用 N/A" RESULT="N/A,N/A,N/A" fi IFS=',' read -r IOPS_RAW BW_RAW LAT_RAW <<< "$RESULT" # 写入 CSV devices_quoted=$(printf '%s' "${DEVICE_ARRAY[*]}" | sed 's/,/","/g') timestamp_log="$(date '+%Y-%m-%d %H:%M:%S')" { echo "$timestamp_log,$TEST_TYPE,\"$devices_quoted\",$RW,$BS,$IODEPTH,$NUMJOBS,$SIZE,$IOENGINE,$RUNTIME,\"$IOPS_RAW\",\"$BW_RAW\",\"$LAT_RAW\"" } >> "$CSV_FILE" # 写入 log printf "%s | %-50s -> IOPS=%-8s, BW=%-10s, Lat=%-8s\n" \ "$timestamp_log" "$JOBNAME" "$IOPS_RAW" "$BW_RAW" "$LAT_RAW" >> "$LOG_FILE" # 终端显示 echo "📊 提取结果: IOPS=$IOPS_RAW, BW=$BW_RAW, Lat=$LAT_RAW" # 软链接 + 完成提示 ln -sf "$TIMESTAMP_FULL" "$OUTPUT_DIR/latest" 2>/dev/null || true echo "" echo "🎉 测试成功完成!" echo "📁 目录: $RUN_DIR" echo "📄 报告: $TXT_OUT" echo "📊 摘要: $LOG_FILE" echo "📈 汇总: $CSV_FILE" echo "❗ 日志: $ERROR_LOG" 将以上代码提取lat修改为这条命令:local lat_raw=$(echo "$lat_line" |grep -i "\(lat.*avg\|clat.*avg\)" | cut -d= -f4 |sed -n '3p' |cut -d, -f1 |xargs) 把没用的lat提取命令去掉!然后提供完整代码给我!
11-12
你希望将原始脚本中用于提取延迟(`latency`)的命令修改为: ```bash local lat_raw=$(echo "$lat_line" |grep -i "\(lat.*avg\|clat.*avg\)" | cut -d= -f4 |sed -n '3p' |cut -d, -f1 |xargs) ``` 并且**删除所有其他无用的 latency 提取逻辑**,只保留这一条高精度提取方式。 --- 我们来分析一下你的新命令的作用: ```bash cut -d= -f4 → 取第4个以 "=" 分隔的字段 sed -n '3p' → 只输出第3行 cut -d, -f1 → 取第一个以逗号分隔的字段(去掉单位如 ns/us/ms 后面的内容?) xargs → 去除前后空白 ``` 但注意: - `grep -i "lat.*avg\|clat.*avg"` 本身可能匹配多行(比如 `lat (usec)` 和 `clat (usec)` 都会出现 `avg` 字段) - 然后你对 `$lat_line` 再次 `grep`,这就变成了“在已经过滤过的单行上再 grep”——这会导致空结果! 所以原设计是先通过: ```bash local lat_line=$(grep -i "\(lat.*avg\|clat.*avg\)" "$file" | head -1) ``` 得到第一行符合条件的行(例如:` clat (usec): min=2, max=100000, avg=45.67, stdev=12.3`) 而你现在想改成从 **多个匹配行中取第3行**,说明你想跳过前两个 `lat` 行,获取更深层统计信息(可能是 `slat`, `clat`, `lat` 中的第三个),这是合理的。 所以我们需要: 1. **不要提前用 `head -1` 截断** 2. 改成从完整输出中找前几条 `lat.*avg` 或 `clat.*avg` 的行,取第3条 3. 再从中提取 `avg=` 后面的值(第4个 `=` 字段?不准确!应基于正则) 但是你当前使用 `cut -d= -f4` 是非常脆弱的方式。因为不同行格式不一致,比如: ``` lat (nsec): min=1, max=1000, avg=450, stdev=200 clat (usec): min=1, max=2000, avg=67.89, stdev=30.1 lat (usec): min=10, max=3000, avg=123.45, stdev=50.2 ``` 如果我们 `grep -i 'lat.*avg'`,会返回三行。`sed -n '3p'` 就是取第三行(最终的 `lat` 综合延迟)。然后你想从该行中提取 `avg=xxx` 的数值部分。 ✅ 更健壮的做法是:**从第3条匹配行中提取 `avg=` 后面的数字+单位** --- ### ✅ 正确且稳定的修改方案如下: 替换整个 `parse_fio_result_stable()` 函数中的 `lat_raw` 提取部分为: ```bash # 查找包含 lat 或 clat 的 avg 行,并取第3行(通常是综合 lat) local lat_lines mapfile -t lat_lines < <(grep -i "\(lat\|clat\).*avg" "$file" | head -3) if [ ${#lat_lines[@]} -ge 3 ]; then local target_line="${lat_lines[2]}" # 第3行 (index 2) local lat_raw=$(echo "$target_line" | sed -n 's/.*avg=\([^,]*\).*/\1/p' | xargs || echo "N/A") else local lat_raw="N/A" fi ``` 但这和你要的“简洁一行式”不符。 --- ### 如果你坚持要使用你提供的那一行命令,并确保它能工作,请看修正版: #### ❌ 原始写法错误点: ```bash local lat_raw=$(echo "$lat_line" | grep -i ... ) ``` → `$lat_line` 已经是 `head -1` 的一行了,再 `grep` 就没输出了! --- ## ✅ 最终解决方案:提供完整修改后的脚本(仅更新 `parse_fio_result_stable` 函数) > ✅ 移除旧的 `lat_line=$(...head -1)` > ✅ 使用你的意图:提取第3个 `lat.*avg` 类似的行,再用 `cut -d= -f4` 取第四个字段,再取 `,` 前内容 --- ### ✅ 修改后的完整脚本(关键变更已标注) ```bash #!/bin/bash #================================================== # 🔧 通用磁盘性能测试脚本(增强版 | 带预热 + TXT报告 | 原始值提取) # # 🚀 使用方法: # ./disk_test.sh sda rw=read --time=300 # ./disk_test.sh nvme0n1 rw=randwrite runtime=600 # ./disk_test.sh -nowarmup sda rw=randread # 跳过预热 # ./disk_test.sh sda runtime=300 rw=randread bs=4k size=100% iodepth=64 ioengine=libaio numjobs=8 # # ✅ 特性: # - 不做任何单位换算,直接提取原始 IOPS/BW/延迟 字符串 # - 支持 /dev/sda, /dev/nvme0n1 等所有块设备 # - 所有结果保存至以时间戳命名的独立子目录 # - 自动提取第6行 IOPS/BW,第9行 avg 延迟(保持原格式) # - 纯 Bash + awk/sed 解析文本,不依赖 bc/jq #================================================== # 默认参数 DEFAULT_BS="4k" DEFAULT_IODEPTH=8 DEFAULT_NUMJOBS=1 DEFAULT_RW="read" DEFAULT_RUNTIME=60 DEFAULT_RAMP_TIME=5 DEFAULT_IOENGINE="libaio" DEFAULT_DIRECT=1 DEFAULT_SIZE="100%" # 输出根目录 OUTPUT_DIR="./fio_results" mkdir -p "$OUTPUT_DIR" # 全局 CSV 表头 CSV_FILE="$OUTPUT_DIR/fio_summary.csv" if [ ! -f "$CSV_FILE" ]; then echo "timestamp,test_type,devices,rw,bs,iodepth,numjobs,size,ioengine,runtime_sec,iops_str,bw_str,latency_str" > "$CSV_FILE" fi #-------------------------------------------------- # 显示帮助信息 show_help() { awk '/^#.*🚀.*使用方法/,/^#.*💡.*提示/' "$0" | sed 's/^# //' exit 0 } for arg in "$@"; do [[ "$arg" == "-h" || "$arg" == "--help" ]] && show_help; done #-------------------------------------------------- # 参数解析 #-------------------------------------------------- WARMUP_MODE="" DEVICE_LIST_STR="" SIZE="$DEFAULT_SIZE" IOENGINE="$DEFAULT_IOENGINE" RUNTIME="$DEFAULT_RUNTIME" RAMP_TIME="$DEFAULT_RAMP_TIME" DIRECT="$DEFAULT_DIRECT" CUSTOM_JOBNAME="" BS="" NUMJOBS="" IODEPTH="" RW="" while [ $# -gt 0 ]; do arg="$1"; shift case "$arg" in -warmup) WARMUP_MODE="force" ;; -nowarmup) WARMUP_MODE="skip" ;; bs=*) BS="${arg#*=}" ;; iodepth=*) IODEPTH="${arg#*=}" ;; numjobs=*) NUMJOBS="${arg#*=}" ;; rw=*) RW="${arg#*=}" ;; runtime=*) RUNTIME="${arg#*=}" ;; --time=*|time=*) RUNTIME="${arg#*=}" ;; ramp_time=*) RAMP_TIME="${arg#*=}" ;; --ioengine=*|ioengine=*) IOENGINE="${arg#*=}" ;; direct=*) DIRECT="${arg#*=}" ;; jobname=*) CUSTOM_JOBNAME="${arg#*=}" ;; size=*) SIZE="${arg#*=}" ;; *) if [ -z "$DEVICE_LIST_STR" ]; then DEVICE_LIST_STR="$arg" else echo "⚠️ 忽略未知参数: $arg" fi ;; esac done # 设置默认值 BS="${BS:-$DEFAULT_BS}" IODEPTH="${IODEPTH:-$DEFAULT_IODEPTH}" NUMJOBS="${NUMJOBS:-$DEFAULT_NUMJOBS}" RW="${RW:-$DEFAULT_RW}" RUNTIME="${RUNTIME:-$DEFAULT_RUNTIME}" RAMP_TIME="${RAMP_TIME:-$DEFAULT_RAMP_TIME}" IOENGINE="${IOENGINE:-$DEFAULT_IOENGINE}" DIRECT="${DIRECT:-$DEFAULT_DIRECT}" SIZE="${SIZE:-$DEFAULT_SIZE}" # 检查设备 if [ -z "$DEVICE_LIST_STR" ]; then echo "❌ 错误:未指定任何设备!" show_help fi is_valid_device_short() { [[ "$1" =~ ^sd[a-z]+$ ]] || [[ "$1" =~ ^nvme[0-9]+n[0-9]+$ ]] } IFS=',' read -ra DEVICES_INPUT <<< "$DEVICE_LIST_STR" DEVICE_ARRAY=() for dev in "${DEVICES_INPUT[@]}"; do dev=$(echo "$dev" | xargs) full_dev="${dev#/dev/}"; full_dev="/dev/$full_dev" [[ "$dev" == /dev/* ]] && full_dev="$dev" if ! is_valid_device_short "$(basename "$full_dev")" && [[ ! -b "$full_dev" ]]; then echo "❌ 设备无效或不存在: $full_dev" exit 1 fi DEVICE_ARRAY+=("$full_dev") done # 检查 fio if ! command -v fio &> /dev/null; then echo "❌ 错误:fio 未安装,请运行 sudo apt install fio" exit 1 fi # 创建运行目录 TIMESTAMP_FULL="$(date '+%Y%m%d_%H%M%S')" DATE_STAMP="$(date '+%Y-%m-%d %H:%M:%S')" RUN_DIR="$OUTPUT_DIR/$TIMESTAMP_FULL" mkdir -p "$RUN_DIR" JOBNAME="${CUSTOM_JOBNAME:-${RW}_bs${BS}_iod${IODEPTH}_nj${NUMJOBS}_sz${SIZE}_eng_${IOENGINE##*/}_t${RUNTIME}s_${TIMESTAMP_FULL}}" TXT_OUT="$RUN_DIR/${JOBNAME}.txt" LOG_FILE="$RUN_DIR/summary.log" ERROR_LOG="$RUN_DIR/error.log" > "$LOG_FILE" [[ ! -f "$ERROR_LOG" ]] && echo "# FIO Error Log - $DATE_STAMP" > "$ERROR_LOG" TEST_TYPE="single_disk" (( ${#DEVICE_ARRAY[@]} > 1 )) && TEST_TYPE="concurrent_multi_disk" # 打印配置摘要 echo "🔧 主测试配置" printf " %-12s : %s\n" "Devices" "$(IFS=','; echo "${DEVICE_ARRAY[*]}")" printf " %-12s : %s\n" "RW Mode" "$RW" printf " %-12s : %s\n" "Block Size" "$BS" printf " %-12s : %s\n" "I/O Depth" "$IODEPTH" printf " %-12s : %s\n" "Num Jobs" "$NUMJOBS" printf " %-12s : %ss\n" "Runtime" "$RUNTIME" printf " %-12s : %s\n" "Test Range" "$SIZE" printf " %-12s : %s\n" "I/O Engine" "$IOENGINE" printf " %-12s : %s\n" "Test Type" "$TEST_TYPE" #-------------------------------------------------- # 预热逻辑 #-------------------------------------------------- prompt_warmup() { if [[ "$WARMUP_MODE" == "skip" ]]; then return 1; fi if [[ "$WARMUP_MODE" == "force" ]]; then return 0; fi echo "" echo "🔥 是否预热?" echo " 1) 是(推荐)" echo " 2) 否" while true; do read -p "选择 [1/2] > " ch case "$ch" in 1) return 0 ;; 2) return 1 ;; esac done } run_warmup() { local err="/tmp/fio_warmup_err_$$.log" > "$err" echo "🔄 预热中..." fio --name=warmup --rw=mixrandrw --rwmixread=70 --bs=4k --iodepth=16 --direct=1 \ --ioengine="$IOENGINE" --size="$SIZE" --output-format=normal \ $(printf -- '--filename=%s ' "${DEVICE_ARRAY[@]}") \ $( (( ${#DEVICE_ARRAY[@]} > 1 )) && echo "--group_reporting" ) \ > /dev/null 2>"$err" || echo "⚠️ 预热失败(详情见日志)" rm -f "$err" } prompt_warmup && run_warmup #-------------------------------------------------- # 执行主测试 #-------------------------------------------------- ERROR_CAPTURE="/tmp/fio_err_$$.log" > "$ERROR_CAPTURE" FIO_CMD=( fio --rw="$RW" --bs="$BS" --direct="$DIRECT" --ioengine="$IOENGINE" --iodepth="$IODEPTH" --numjobs="$NUMJOBS" --runtime="$RUNTIME" --ramp_time="$RAMP_TIME" --size="$SIZE" --time_based --output-format=normal --output="$TXT_OUT" ) for dev in "${DEVICE_ARRAY[@]}"; do FIO_CMD+=(--filename="$dev") done if (( ${#DEVICE_ARRAY[@]} > 1 )); then FIO_CMD+=(--group_reporting --name=multi_device_test) else FIO_CMD+=(--name="$JOBNAME") fi echo "" echo "🚀 正在运行主测试: $JOBNAME ..." echo "⏱️ 开始时间: $DATE_STAMP" if "${FIO_CMD[@]}" > /dev/null 2>"$ERROR_CAPTURE"; then echo "✅ 主测试完成" else echo "❌ fio 测试失败!返回码: $?" { echo "=======================================" echo "Timestamp: $DATE_STAMP" echo "Test Name: $JOBNAME" echo "Command: ${FIO_CMD[*]}" echo "--- Error Output ---" cat "$ERROR_CAPTURE" echo "" } >> "$ERROR_LOG" echo "📝 错误已记录到: $ERROR_LOG" rm -f "$ERROR_CAPTURE" exit 1 fi rm -f "$ERROR_CAPTURE" #-------------------------------------------------- # ✅ 统一提取函数:所有字段使用相同高可靠正则 #-------------------------------------------------- parse_fio_result_stable() { local file="$1" # 查找包含吞吐信息的行(读/写行 或 group_report) local throughput_line throughput_line=$(grep -i "\(iops\|bw\)" "$file" | grep -E "(^[[:space:]]*[rw].*:)|(group_report)" | head -1) # ✅ 统一使用 grep -oiE 提取字段(不区分大小写,精确匹配到分隔符前) local iops_raw=$(echo "$throughput_line" | grep -oiE "iops=[^,]*[^,[:space:]]" | cut -d= -f2 | xargs || echo "N/A") local bw_raw=$(echo "$throughput_line" | grep -oiE "bw=[^,]*[^,[:space:]]B.s" | cut -d= -f2 | xargs || echo "N/A") # ✅ 替换为你指定的 lat_raw 提取方式(改进版:避免在单行上重复 grep) # 原来的 lat_line 已被移除,直接从文件中处理 local lat_raw=$(grep -i "\(lat.*avg\|clat.*avg\)" "$file" | cut -d= -f4 | sed -n '3p' | cut -d, -f1 | xargs || echo "N/A") # 返回三者 echo "$iops_raw,$bw_raw,$lat_raw" } # 执行提取 echo "" RESULT=$(parse_fio_result_stable "$TXT_OUT") if [ -z "$RESULT" ] || ! echo "$RESULT" | grep -q ","; then echo "⚠️ 提取失败,使用 N/A" RESULT="N/A,N/A,N/A" fi IFS=',' read -r IOPS_RAW BW_RAW LAT_RAW <<< "$RESULT" # 写入 CSV devices_quoted=$(printf '%s' "${DEVICE_ARRAY[*]}" | sed 's/,/","/g') timestamp_log="$(date '+%Y-%m-%d %H:%M:%S')" { echo "$timestamp_log,$TEST_TYPE,\"$devices_quoted\",$RW,$BS,$IODEPTH,$NUMJOBS,$SIZE,$IOENGINE,$RUNTIME,\"$IOPS_RAW\",\"$BW_RAW\",\"$LAT_RAW\"" } >> "$CSV_FILE" # 写入 log printf "%s | %-50s -> IOPS=%-8s, BW=%-10s, Lat=%-8s\n" \ "$timestamp_log" "$JOBNAME" "$IOPS_RAW" "$BW_RAW" "$LAT_RAW" >> "$LOG_FILE" # 终端显示 echo "📊 提取结果: IOPS=$IOPS_RAW, BW=$BW_RAW, Lat=$LAT_RAW" # 软链接 + 完成提示 ln -sf "$TIMESTAMP_FULL" "$OUTPUT_DIR/latest" 2>/dev/null || true echo "" echo "🎉 测试成功完成!" echo "📁 目录: $RUN_DIR" echo "📄 报告: $TXT_OUT" echo "📊 摘要: $LOG_FILE" echo "📈 汇总: $CSV_FILE" echo "❗ 日志: $ERROR_LOG" ``` --- ### ✅ 修改说明总结: | 修改点 | 说明 | |------|------| | 删除 `local lat_line=...head -1` | 避免提前截断,让后续 `grep` 能看到全部行 | | 更新 `lat_raw` 提取命令 | 使用你要求的管道链:<br>`grep -i ... | cut -d= -f4 | sed -n '3p' | cut -d, -f1 | xargs` | | 保证容错 | 添加 `|| echo "N/A"` 防止变量为空 | --- ### ⚠️ 注意事项: - `cut -d= -f4` 假设每行都有至少4个 `=` 字段(如 `min=..., max=..., avg=..., stdev=...`),否则会出错。 - 若某行缺少字段(如只有 `avg=123`),可能导致错位。 - 推荐未来改用正则提取:`sed -n 's/.*avg=\([^,]*\).*/\1/p'` --- ### ✅ 示例输出行为(假设 fio 输出中有三行含 avg): ``` slat (usec): ... avg=5.22 ... clat (usec): ... avg=40.11 ... lat (usec): ... avg=45.33, ... ``` → `cut -d= -f4` → 第4个等号字段是 `45.33`(前面 slat 有3个=,clat有3个=,lat第4个才是 avg) 所以这个方法在标准输出下是可行的。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值