feat: 更新

这个提交包含在:
HA
2026-04-26 23:56:39 +08:00
父节点 8d03fbac89
当前提交 99c9c69862
共有 2 个文件被更改,包括 70 次插入100 次删除

查看文件

@@ -459,29 +459,18 @@ cleanup_local() {
smb_check_deps() {
dbg "smb_check_deps: OS=$OS_NAME"
case "$OS_NAME" in
Linux)
if command -v smbclient >/dev/null 2>&1; then
SMB_TOOL="smbclient"
dbg "smb_check_deps: 找到 smbclient => $(command -v smbclient)"
else
err "未安装 smbclient。Debian/Ubuntu: apt install smbclient;RHEL/Alma: dnf install samba-client"
return 1
fi
;;
Darwin)
if command -v smbclient >/dev/null 2>&1; then
SMB_TOOL="smbclient"
dbg "smb_check_deps: 找到 smbclient => $(command -v smbclient)"
elif command -v mount_smbfs >/dev/null 2>&1; then
SMB_TOOL="mount_smbfs"
dbg "smb_check_deps: 未找到 smbclient,回退 mount_smbfs => $(command -v mount_smbfs)"
else
err "未找到 smbclientbrew install samba或 mount_smbfs"
return 1
fi
;;
Linux|Darwin) ;;
*) err "不支持的系统:$OS_NAME"; return 1 ;;
esac
if ! command -v smbclient >/dev/null 2>&1; then
case "$OS_NAME" in
Linux) err "未安装 smbclient。Debian/Ubuntu: apt install smbclient;RHEL/Alma: dnf install samba-client" ;;
Darwin) err "未安装 smbclient。请先安装brew install samba" ;;
esac
return 1
fi
SMB_TOOL="smbclient"
dbg "smb_check_deps: 找到 smbclient => $(command -v smbclient)"
log "SMB 工具:$SMB_TOOL"
return 0
}
@@ -498,9 +487,21 @@ smb_validate() {
smb_upload_smbclient() {
dbg "smb_upload_smbclient: 进入,本次文件数=${#ARCHIVE_FILES[@]} 备份目录=$ARCHIVE_BASENAME"
local remote_base="${SMB_PATH%/}"
local commands=""
local archive_dir
if [[ -n "$remote_base" ]]; then
archive_dir="${remote_base}/${ARCHIVE_BASENAME}"
else
archive_dir="${ARCHIVE_BASENAME}"
fi
# 逐级 mkdir 共享内的 SMB_PATH可能不存在
local args=( "//${SMB_HOST}/${SMB_SHARE}" "-U" "${SMB_USER}%${SMB_PASSWORD}" )
[[ -n "$SMB_DOMAIN" ]] && args+=( "-W" "$SMB_DOMAIN" )
[[ -n "$SMB_VERSION" ]] && args+=( "-m" "SMB${SMB_VERSION//./}" )
local sm_debug=()
[[ "$DEBUG" == "true" ]] && sm_debug=( "-d" "1" )
# 第 1 步:建目录(一次连接,逐级 mkdir SMB_PATH 再 mkdir 本次备份子目录)
local mkdir_cmd=""
if [[ -n "$remote_base" ]]; then
local IFS=/
# shellcheck disable=SC2206
@@ -510,35 +511,38 @@ smb_upload_smbclient() {
for seg in "${segs[@]}"; do
[[ -z "$seg" ]] && continue
cur="${cur}${seg}"
commands+="mkdir \"${cur}\";"
mkdir_cmd+="mkdir \"${cur}\";"
cur="${cur}/"
done
commands+="cd \"${remote_base}\";"
mkdir_cmd+="cd \"${remote_base}\";"
fi
# 为本次备份创建独立子目录
commands+="mkdir \"${ARCHIVE_BASENAME}\";cd \"${ARCHIVE_BASENAME}\";"
mkdir_cmd+="mkdir \"${ARCHIVE_BASENAME}\";"
local pp pname
for pp in "${ARCHIVE_FILES[@]}"; do
pname="$(basename "$pp")"
commands+="put \"${pp}\" \"${pname}\";"
done
dbg "smb_upload_smbclient: smbclient 命令串=$commands"
local args=( "//${SMB_HOST}/${SMB_SHARE}" "-U" "${SMB_USER}%${SMB_PASSWORD}" )
[[ -n "$SMB_DOMAIN" ]] && args+=( "-W" "$SMB_DOMAIN" )
[[ -n "$SMB_VERSION" ]] && args+=( "-m" "SMB${SMB_VERSION//./}" )
# debug 模式给 smbclient 也加上 -d 1
local sm_debug=()
[[ "$DEBUG" == "true" ]] && sm_debug=( "-d" "1" )
log "通过 smbclient 上传到 //${SMB_HOST}/${SMB_SHARE}/${SMB_PATH%/}/${ARCHIVE_BASENAME}/"
log "通过 smbclient 上传到 //${SMB_HOST}/${SMB_SHARE}/${archive_dir}/(共 ${#ARCHIVE_FILES[@]} 个文件)"
dbg "smbclient 参数(脱敏)://${SMB_HOST}/${SMB_SHARE} -U ${SMB_USER}%$(mask "$SMB_PASSWORD") ${SMB_DOMAIN:+-W $SMB_DOMAIN} ${SMB_VERSION:+-m SMB${SMB_VERSION//./}}"
if ! smbclient "${args[@]}" "${sm_debug[@]}" -c "$commands"; then
err "smbclient 上传失败"
return 1
fi
dbg "smb_upload_smbclient: 上传 OK"
dbg "smbclient mkdir 命令串=$mkdir_cmd"
# mkdir 失败可能是已存在,不致命;smbclient 不会非 0 退出,这里只做一次连接确保目录就绪
smbclient "${args[@]}" "${sm_debug[@]}" -c "$mkdir_cmd" >/dev/null 2>&1 || true
# 第 2 步:每个文件单独一次 smbclient 调用,方便打印 [i/N] 进度与单文件耗时
local total=${#ARCHIVE_FILES[@]}
local idx=0
local pp pname pkb start_ts elapsed
for pp in "${ARCHIVE_FILES[@]}"; do
idx=$((idx+1))
pname="$(basename "$pp")"
pkb="$(du -k "$pp" 2>/dev/null | awk '{print $1+0}')"
log "上传 [${idx}/${total}]$pname ($(awk -v k="$pkb" 'BEGIN{if(k>=1024*1024)printf "%.2fG", k/1024/1024; else if(k>=1024)printf "%.1fM", k/1024; else printf "%dK", k}'))"
start_ts=$(date +%s)
if ! smbclient "${args[@]}" "${sm_debug[@]}" -D "$archive_dir" -c "put \"${pp}\" \"${pname}\""; then
elapsed=$(( $(date +%s) - start_ts ))
err "上传失败 [${idx}/${total}]$pp (耗时 ${elapsed}s)"
return 1
fi
elapsed=$(( $(date +%s) - start_ts ))
ok "上传完成 [${idx}/${total}]$pname (耗时 ${elapsed}s)"
done
ok "已上传全部 ${total} 个文件到 //${SMB_HOST}/${SMB_SHARE}/${archive_dir}/"
if [[ "$COMMON_RETENTION_DAYS" -gt 0 ]]; then
smb_retention_smbclient "${args[@]}"
@@ -584,43 +588,6 @@ smb_retention_smbclient() {
done
}
smb_upload_mount() {
dbg "smb_upload_mount: 使用 mount_smbfs 兜底"
local mnt
mnt="$(mktemp -d /tmp/smbmnt.XXXXXX)"
local url="//${SMB_USER}:${SMB_PASSWORD}@${SMB_HOST}/${SMB_SHARE}"
log "挂载 SMB${mnt}"
dbg "mount_smbfs URL脱敏//${SMB_USER}:$(mask "$SMB_PASSWORD")@${SMB_HOST}/${SMB_SHARE}"
if ! mount_smbfs "$url" "$mnt"; then
err "挂载 SMB 失败"; rmdir "$mnt"; return 1
fi
local dest="$mnt"
if [[ -n "$SMB_PATH" ]]; then
dest="$mnt/$SMB_PATH"
fi
dest="$dest/$ARCHIVE_BASENAME"
if ! mkdir -p "$dest"; then
err "无法创建远端目录:$dest"
umount "$mnt" 2>/dev/null
rmdir "$mnt"
return 1
fi
dbg "smb_upload_mount: 目标 $dest,待上传=${#ARCHIVE_FILES[@]}"
local pp
for pp in "${ARCHIVE_FILES[@]}"; do
log "复制:$(basename "$pp")"
if ! cp "$pp" "$dest/"; then
err "复制失败:$pp"
umount "$mnt" 2>/dev/null
rmdir "$mnt"
return 1
fi
done
ok "已复制全部 ${#ARCHIVE_FILES[@]} 个文件到 $dest/"
umount "$mnt" 2>/dev/null
rmdir "$mnt"
}
run_smb() {
dbg "run_smb: 开始"
smb_check_deps || return 1
@@ -628,13 +595,8 @@ run_smb() {
print_effective_config
create_archive || return 1
if [[ "$SMB_TOOL" == "smbclient" ]]; then
smb_upload_smbclient || { cleanup_local; return 1; }
else
smb_upload_mount || { cleanup_local; return 1; }
fi
smb_upload_smbclient || return 1
ok "SMB 上传完成"
cleanup_local
}
# ---------- rclone ----------
@@ -697,26 +659,29 @@ rclone_upload() {
dbg "rclone_upload: 待上传=${#ARCHIVE_FILES[@]} 远端目录=${RCLONE_REMOTE}:${RCLONE_PATH%/}/${ARCHIVE_BASENAME}/"
local remote_dir="${RCLONE_REMOTE}:${RCLONE_PATH%/}/${ARCHIVE_BASENAME}"
log "通过 rclone 上传到 ${remote_dir}/"
log "通过 rclone 上传到 ${remote_dir}/(共 ${#ARCHIVE_FILES[@]} 个文件)"
# 让 rclone 每 5 秒打印一行进度(已传/总量/百分比/速率/ETA,写到 stdout 而不是被 INFO 级别压住。
# --stats-one-line 让进度落在单行;NOTICE 级别能在非交互cron下也输出。
local stats_flags=( --stats=5s --stats-one-line --stats-log-level NOTICE )
local total=${#ARCHIVE_FILES[@]}
local idx=0
local pp pname pkb start_ts elapsed rc
for pp in "${ARCHIVE_FILES[@]}"; do
idx=$((idx+1))
pname="$(basename "$pp")"
pkb="$(du -k "$pp" 2>/dev/null | awk '{print $1+0}')"
log "上传:$pname ($(awk -v k="$pkb" 'BEGIN{if(k>=1024*1024)printf "%.2fG", k/1024/1024; else if(k>=1024)printf "%.1fM", k/1024; else printf "%dK", k}'))"
log "上传 [${idx}/${total}]$pname ($(awk -v k="$pkb" 'BEGIN{if(k>=1024*1024)printf "%.2fG", k/1024/1024; else if(k>=1024)printf "%.1fM", k/1024; else printf "%dK", k}'))"
start_ts=$(date +%s)
rclone_cmd copyto "${stats_flags[@]}" "$pp" "${remote_dir}/${pname}"
rc=$?
elapsed=$(( $(date +%s) - start_ts ))
if [[ $rc -ne 0 ]]; then
err "rclone 上传失败:$pp (耗时 ${elapsed}s)"
err "上传失败 [${idx}/${total}]$pp (耗时 ${elapsed}s)"
return 1
fi
ok "上传完成:$pname (耗时 ${elapsed}s)"
ok "上传完成 [${idx}/${total}]$pname (耗时 ${elapsed}s)"
done
ok "已上传全部 ${#ARCHIVE_FILES[@]} 个文件到 ${remote_dir}/"
ok "已上传全部 ${total} 个文件到 ${remote_dir}/"
if [[ "$COMMON_RETENTION_DAYS" -gt 0 ]]; then
rclone_retention
@@ -772,9 +737,8 @@ run_rclone() {
print_effective_config
create_archive || return 1
rclone_upload || { cleanup_local; return 1; }
rclone_upload || return 1
ok "rclone 上传完成"
cleanup_local
}
# ---------- SFTP预留----------
@@ -797,6 +761,12 @@ main() {
parse_args "$@" || exit 1
# 任何退出路径(成功 / 失败 / Ctrl-C / kill都触发一次清理cleanup_local 自身
# 受 COMMON_CLEAN_LOCAL 控制,且只删 ${COMMON_ARCHIVE_PREFIX}* 匹配项,幂等可重入。
trap 'cleanup_local' EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
print_effective_config
case "$METHOD" in