#!/usr/bin/env bash # docs-sync-check.sh — 对账「本机文档镜像」与「服务器文档库」 # # 用法: # bash scripts/docs-sync-check.sh # 报告双端差异 # DOCS_REMOTE=bt-server bash scripts/docs-sync-check.sh # # 可覆盖的环境变量: # DOCS_LOCAL_DIR 本机镜像目录(默认 E:/ProgramData/AI技能/aliyun-dsh-server/dsh-server-docs) # DOCS_REMOTE ssh 别名或 host(默认 bt-server) # DOCS_REMOTE_DIR 服务器文档目录(默认 /opt/dsh/docs) # # 退出码:0 = 完全一致;1 = 存在差异;2 = 环境/连接错误 set -uo pipefail LOCAL_DIR="${DOCS_LOCAL_DIR:-E:/ProgramData/AI技能/aliyun-dsh-server/dsh-server-docs}" REMOTE="${DOCS_REMOTE:-bt-server}" REMOTE_DIR="${DOCS_REMOTE_DIR:-/opt/dsh/docs}" [ -d "$LOCAL_DIR" ] || { echo "ERROR: 本机目录不存在: $LOCAL_DIR" >&2; exit 2; } tmp_l="$(mktemp)"; tmp_r="$(mktemp)" trap 'rm -f "$tmp_l" "$tmp_r" 2>/dev/null || true' EXIT hash_local() { ( cd "$LOCAL_DIR" && find . -type f ! -path './.git/*' ! -name '.DS_Store' | LC_ALL=C sort | while read -r f; do printf '%s\t%s\n' "${f#./}" "$(md5sum "$f" | cut -d' ' -f1)" done ) } hash_remote() { ssh -o BatchMode=yes -o ConnectTimeout=10 "$REMOTE" \ "cd '$REMOTE_DIR' 2>/dev/null && find . -type f ! -path './.git/*' ! -name '.DS_Store' | LC_ALL=C sort | while read -r f; do printf '%s\t%s\n' \"\${f#./}\" \"\$(md5sum \"\$f\" | cut -d' ' -f1)\"; done" \ 2>/dev/null } hash_local > "$tmp_l" hash_remote > "$tmp_r" if [ ! -s "$tmp_r" ]; then echo "ERROR: 无法读取服务器目录 $REMOTE:$REMOTE_DIR(ssh 失败或目录不存在)" >&2 exit 2 fi awk -F'\t' ' NR==FNR { l[$1]=$2; next } { r[$1]=$2 } END { same=0 for (k in l) { if (k in r) { if (l[k]==r[k]) same++; else { diff++; print "⚠️ 内容不一致 " k } } else { only_l++; print "⬆️ 仅本地(待推送) " k } } for (k in r) if (!(k in l)) { only_r++; print "⬇️ 仅服务器(待拉取) " k } printf "\n—— 汇总 ——\n 一致: %d\n 内容不一致: %d\n 仅本地: %d\n 仅服务器: %d\n 本地文件总数: %d / 服务器文件总数: %d\n", same, diff+0, only_l+0, only_r+0, length(l), length(r) if ((diff+0)+(only_l+0)+(only_r+0) > 0) { print "\n结果: 存在差异 ❌"; exit 1 } print "\n结果: 双端一致 ✅" } ' "$tmp_l" "$tmp_r"