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
相关产品推荐
相关产品推荐

