You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bash脚本多命令错误处理优化:家用温控器控制场景

家用温控器Bash脚本的优雅错误处理实现

需求背景

夏季峰谷电价触发的cron任务逻辑:

  • 峰期:关闭空调并生成~/therm/.therm-tod-override-enabled文件
  • 峰期结束后(每日19:02,工作日5-9月):运行目标脚本,仅当override文件存在且空调处于关闭状态时,恢复制冷模式并删除该文件,否则不执行操作

现有问题

脚本偶发异常:override文件已删除,但空调未恢复制冷。根源可能是:

  • USB转串口设备偶发通信错误,导致温控器状态读取失败
  • 温控器返回的XML格式异常,导致xmlstarlet解析失败

错误处理需求

  • 初始XML读取最多尝试3次,全部失败则写入Debian系统错误日志
  • xmlstarlet解析失败时立即终止脚本并记录错误
  • 空调模式设置操作失败时记录错误到Debian日志

现有实现代码

当前运行脚本

#!/bin/bash

# Runs from cron only M-F May-September at 7:02p.  If watchfile found and mode still off, have thermostat resume cooling.

# Poll the thermostat, format the result in XML, and drop the XML in a file
~/therm/omnistat -d /dev/ttyUSB0 -x > ~/therm/therm.xml
# Pull thermostat mode from XML, values can be Heat, Cool, Off
thermode=$(xmlstarlet sel -t -v "/therm/name_mode" ~/therm/therm.xml)
# Check for existing override file generated by turn-off script, value is 1 or 0.
# Thermostat program does the actual file check for other unrelated PHP use-case reasons.
isoverride=$(xmlstarlet sel -t -v "/therm/tod_override" ~/therm/therm.xml)
   if [[ "$thermode" == "Off" && "$isoverride" == "1" ]]; then
# Set mode back to Cool
     ~/therm/omnistat -d /dev/ttyUSB0 -M C
# Remove the override-enabled touch file
     rm ~/therm/.therm-tod-override-enabled
   fi

此前的简单重试尝试

loopcount=0
exterrlev=44
while [ $exterrlev -ne 0 ] && [ $loopcount -lt 4 ]; do
    ~/therm/omnistat -d /dev/ttyUSB0 -M O
    exterrlev=$?
    loopcount=$((loopcount+1))
    done

优化后的脚本实现

#!/bin/bash
set -eo pipefail

# 配置参数集中管理,便于修改
THERM_DIR="$HOME/therm"
OMNISTAT="$THERM_DIR/omnistat"
THERM_XML="$THERM_DIR/therm.xml"
OVERRIDE_FILE="$THERM_DIR/.therm-tod-override-enabled"
MAX_RETRIES=3
LOG_TAG="therm-resume-cool"

# 统一错误日志函数:写入Debian系统日志(可通过journalctl -t therm-resume-cool查看)
log_error() {
    logger -t "$LOG_TAG" -p err "$1"
}

# 封装温控器XML读取与验证逻辑,支持重试
fetch_therm_xml() {
    local retry_count=0
    while (( retry_count < MAX_RETRIES )); do
        # 尝试读取XML并写入文件
        if "$OMNISTAT" -d /dev/ttyUSB0 -x > "$THERM_XML"; then
            # 验证XML有效性(检查根节点是否存在)
            if xmlstarlet sel -t -v "/therm" "$THERM_XML" &>/dev/null; then
                return 0  # 读取成功,返回
            fi
        fi
        (( retry_count++ ))
        sleep 2  # 重试前等待2秒,避免频繁访问串口设备
    done
    # 重试全部失败,记录错误并退出
    log_error "Failed to fetch valid thermostat XML after $MAX_RETRIES attempts"
    exit 1
}

# 主执行逻辑
# 1. 获取并验证温控器XML
fetch_therm_xml

# 2. 解析XML参数,失败则记录错误并终止
if ! thermode=$(xmlstarlet sel -t -v "/therm/name_mode" "$THERM_XML"); then
    log_error "Failed to parse thermostat mode from XML"
    exit 1
fi

if ! isoverride=$(xmlstarlet sel -t -v "/therm/tod_override" "$THERM_XML"); then
    log_error "Failed to parse TOD override status from XML"
    exit 1
fi

# 3. 检查执行条件:空调关闭、override状态为1、override文件存在
if [[ "$thermode" == "Off" && "$isoverride" == "1" && -f "$OVERRIDE_FILE" ]]; then
    # 设置制冷模式,失败则记录错误且不删除文件(保留重试机会)
    if ! "$OMNISTAT" -d /dev/ttyUSB0 -M C; then
        log_error "Failed to set thermostat to Cool mode"
        exit 1
    fi
    # 删除override文件,失败也记录日志
    if ! rm "$OVERRIDE_FILE"; then
        log_error "Failed to remove override file: $OVERRIDE_FILE"
    fi
fi

优化点说明

  • set -eo pipefail:开启严格错误检查,命令失败或管道出错时立即退出,避免后续逻辑在错误状态下执行
  • 参数集中管理:将路径、重试次数等配置放在脚本顶部,便于后续维护
  • 封装XML读取逻辑:fetch_therm_xml函数实现重试机制,同时验证XML有效性,避免无效XML进入后续解析步骤
  • 解析错误处理:每个xmlstarlet解析命令都检查返回值,失败时立即记录错误并终止脚本
  • 操作失败处理:空调模式设置失败时,不删除override文件,保证下次cron任务可以重试;文件删除失败单独记录日志
  • 条件增强:增加-f "$OVERRIDE_FILE"检查,确保文件存在才执行后续操作,避免误操作

内容的提问来源于stack exchange,提问作者Jason

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 22:05:25