diff --git a/backup.sh b/backup.sh index ebcb206..39ed934 100755 --- a/backup.sh +++ b/backup.sh @@ -4,7 +4,7 @@ # - 打包多目录(支持 pigz),生成 SHA256 校验,obsutil 分片/并发上传 # - 上传 .tar.gz 以及 .tar.gz.sha256,并下载远端 .sha256 比对确保完整性 # - 成功/失败写入日志并 Telegram 通知 -# - 支持 --dry-run / --verify / --keep-local / --concurrency / --rate / --label / --sse +# - 支持 --dry-run / --self-check / --verify / --keep-local / --concurrency / --rate / --label / --sse # 退出码:0 成功;2 配置缺失;3 打包失败;4 上传失败;5 校验失败 set -Eeuo pipefail @@ -81,6 +81,7 @@ BACKUP_PATHS=() KEEP_LOCAL=false DRY_RUN=false +SELF_CHECK=false VERIFY_ONLY=false CLI_LABEL="" CLI_SSE="none" @@ -92,7 +93,7 @@ GPG_SYM=false usage() { cat <<'USAGE' 用法: - ./backup.sh [--dry-run] [--verify] [--keep-local] + ./backup.sh [--dry-run] [--self-check] [--verify] [--keep-local] [--concurrency=N] [--rate=10MB/s] [--label=NAME] [--sse=none|kms|aes256] @@ -103,6 +104,7 @@ USAGE while [[ $# -gt 0 ]]; do case "$1" in --dry-run) DRY_RUN=true; shift;; + --self-check) DRY_RUN=true; SELF_CHECK=true; shift;; --verify) VERIFY_ONLY=true; shift;; --keep-local) KEEP_LOCAL=true; shift;; --concurrency=*) CLI_CONCURRENCY="${1#*=}"; shift;; @@ -524,6 +526,10 @@ if $DRY_RUN; then log "INFO" "Dry-run 模式:不执行上传。归档:${ARCHIVE_BASE} 大小:${BACKUP_SIZE_HUMAN}" log "INFO" "本次总用时:${SECONDS}s" tg_backup_dryrun "$LABEL" "$(date +'%Y-%m-%d %H:%M:%S')" "$(printf '%s\n' "${BACKUP_PATHS[@]}")" "$(tg_read_user_excludes "$USER_EXCLUDE_FILE")" "$ARCHIVE_BASE" "$BACKUP_SIZE_HUMAN" "${PACK_SECONDS}s" + if $SELF_CHECK; then + rm -f "$ARCHIVE_PATH" "$SHA_PATH" || true + log "INFO" "Self-check 模式:已删除本地自检归档与校验文件" + fi exit 0 fi diff --git a/install.sh b/install.sh index 9f8f2e5..f517ac3 100755 --- a/install.sh +++ b/install.sh @@ -14,6 +14,7 @@ usage() { ./install.sh [--user=NAME] [--mode=system|user] [--dry-run] 说明: + - 若未检测到仓库内 obsutil,会自动调用 lib/obsutil.sh 安装 - system 模式会安装到 /etc/systemd/system - user 模式会安装到 ~/.config/systemd/user - 不会覆盖已有 env.conf 和 exclude.user.list @@ -61,6 +62,18 @@ if [[ "$INSTALL_MODE" == "user" ]]; then SYSTEMD_DIR="${HOME_DIR}/.config/systemd/user" fi +if [[ -f "${SCRIPT_DIR}/obsutil" ]]; then + chmod 755 "${SCRIPT_DIR}/obsutil" + echo "已检测到仓库内 obsutil:${SCRIPT_DIR}/obsutil" +else + chmod 755 "${SCRIPT_DIR}/lib/obsutil.sh" + if $DRY_RUN; then + bash "${SCRIPT_DIR}/lib/obsutil.sh" --dry-run + else + bash "${SCRIPT_DIR}/lib/obsutil.sh" + fi +fi + mkdir -p "${SCRIPT_DIR}/systemd" if [[ ! -f "${SCRIPT_DIR}/env.conf" ]]; then @@ -84,10 +97,15 @@ fi chmod 755 \ "${SCRIPT_DIR}/backup.sh" \ "${SCRIPT_DIR}/clean_backups.sh" \ + "${SCRIPT_DIR}/lib/obsutil.sh" \ "${SCRIPT_DIR}/lib/telegram.sh" \ "${SCRIPT_DIR}/install.sh" \ "${SCRIPT_DIR}/update.sh" +if [[ -f "${SCRIPT_DIR}/obsutil" ]]; then + chmod 755 "${SCRIPT_DIR}/obsutil" +fi + if $DRY_RUN; then echo "[dry-run] 目标 systemd 目录:$SYSTEMD_DIR" else diff --git a/lib/obsutil.sh b/lib/obsutil.sh new file mode 100755 index 0000000..c43da0a --- /dev/null +++ b/lib/obsutil.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash +# 下载或更新仓库内的 obsutil 二进制。 +# - 默认安装到仓库根目录下的 ./obsutil +# - 若系统已有 obsutil,不会覆盖;只管理仓库内副本 +# - install.sh 和手动维护都可复用这个脚本 + +set -Eeuo pipefail + +SCRIPT_PATH="${BASH_SOURCE[0]:-$0}" +LIB_DIR="$(cd "$(dirname -- "$SCRIPT_PATH")" && pwd -P)" +ROOT_DIR="$(cd "${LIB_DIR}/.." && pwd -P)" +TARGET_BIN="${ROOT_DIR}/obsutil" +DRY_RUN=false + +usage() { + cat <<'USAGE' +用法: + ./lib/obsutil.sh [--dry-run] + +说明: + - 下载并安装或更新仓库内的 ./obsutil + - 默认覆盖仓库里的旧版本 + - dry-run 只打印将执行的动作,不实际下载 +USAGE +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --dry-run) DRY_RUN=true; shift ;; + -h|--help) usage; exit 0 ;; + *) echo "未知参数:$1" >&2; usage; exit 2 ;; + esac +done + +need_cmd() { + command -v "$1" >/dev/null 2>&1 || { + echo "缺少命令:$1" >&2 + exit 2 + } +} + +download_file() { + local url="$1" + local output="$2" + + if command -v curl >/dev/null 2>&1; then + curl -fsSL "$url" -o "$output" + return 0 + fi + + if command -v wget >/dev/null 2>&1; then + wget -qO "$output" "$url" + return 0 + fi + + echo "缺少下载工具:curl 或 wget" >&2 + exit 2 +} + +resolve_package_name() { + case "$(uname -m)" in + x86_64|amd64) printf '%s\n' "obsutil_linux_amd64.tar.gz" ;; + aarch64|arm64) printf '%s\n' "obsutil_linux_arm64.tar.gz" ;; + *) + echo "不支持的 CPU 架构:$(uname -m)" >&2 + echo "请手动安装 obsutil 后再试" >&2 + exit 2 + ;; + esac +} + +need_cmd install +need_cmd tar + +PACKAGE_NAME="$(resolve_package_name)" +DOWNLOAD_URL="https://obs-community-intl.obs.ap-southeast-1.myhuaweicloud.com/obsutil/current/${PACKAGE_NAME}" + +if $DRY_RUN; then + if [[ -x "$TARGET_BIN" ]]; then + echo "[dry-run] 将更新仓库内 obsutil:${TARGET_BIN}" + else + echo "[dry-run] 将安装仓库内 obsutil:${TARGET_BIN}" + fi + echo "[dry-run] 下载地址:${DOWNLOAD_URL}" + exit 0 +fi + +TMP_DIR="$(mktemp -d)" +ARCHIVE_PATH="${TMP_DIR}/${PACKAGE_NAME}" +trap 'rm -rf "$TMP_DIR"' EXIT + +if [[ -x "$TARGET_BIN" ]]; then + echo "开始更新仓库内 obsutil:${TARGET_BIN}" +else + echo "开始安装仓库内 obsutil:${TARGET_BIN}" +fi + +download_file "$DOWNLOAD_URL" "$ARCHIVE_PATH" +tar -xzf "$ARCHIVE_PATH" -C "$TMP_DIR" + +BINARY_PATH="$(find "$TMP_DIR" -type f -name obsutil | head -n 1)" +if [[ -z "$BINARY_PATH" ]]; then + echo "下载包中未找到 obsutil 可执行文件" >&2 + exit 2 +fi + +install -m 755 "$BINARY_PATH" "$TARGET_BIN" +VERSION_OUTPUT="$("$TARGET_BIN" version 2>/dev/null || true)" +if [[ -z "$VERSION_OUTPUT" ]]; then + echo "obsutil 已安装,但运行校验失败:${TARGET_BIN}" >&2 + exit 2 +fi + +echo "obsutil 已就绪:${TARGET_BIN}" +echo "$VERSION_OUTPUT" | head -n 1 diff --git a/readme.md b/readme.md index a1fad83..5872c17 100644 --- a/readme.md +++ b/readme.md @@ -4,6 +4,7 @@ - `backup.sh`:打包目录、生成 `sha256`、上传到 OBS、下载远端校验文件做一致性校验 - `clean_backups.sh`:按文件名日期清理旧备份 +- `lib/obsutil.sh`:下载或更新仓库根目录下的 `./obsutil` - `lib/telegram.sh`:统一处理 Telegram 通知模板和发送逻辑 ## 配置 @@ -49,6 +50,12 @@ EOF ./backup.sh --dry-run ``` +仅做自检,且不保留本地归档: + +```bash +./backup.sh --self-check +``` + 检查远端前缀是否可访问: ```bash @@ -73,6 +80,7 @@ cd /root/backup_OBS `install.sh` 会自动完成: +- 如果未检测到仓库根目录下的 `./obsutil`,则自动调用 `./lib/obsutil.sh` - 如果不存在,则创建 `env.conf` - 如果不存在,则创建 `exclude.user.list` - 修正脚本执行权限 @@ -93,6 +101,14 @@ cd /root/backup_OBS - `system` 模式需要 root 权限 - `user` 模式会安装到 `~/.config/systemd/user` - 安装脚本不会覆盖已有的 `env.conf` 和 `exclude.user.list` +- `install.sh` 和后续脚本都只使用仓库根目录下的 `./obsutil` +- `obsutil` 安装完成后,首次仍需手动执行 `./obsutil config -interactive` + +如果你想手动安装或更新仓库根目录下的 `./obsutil`,可以直接执行: + +```bash +./lib/obsutil.sh +``` 常用查看命令: @@ -114,7 +130,7 @@ journalctl -u backup-obs-update.service -n 100 - `git fetch --tags origin` - 默认更新 `stable` 分支,或切换到你指定的 tag / 分支 - 修正脚本执行权限 -- 自动执行一次 `./backup.sh --dry-run` 自检 +- 自动执行一次 `./backup.sh --self-check` 自检 也可以手动指定版本: diff --git a/update.sh b/update.sh index aab0794..092a0e3 100755 --- a/update.sh +++ b/update.sh @@ -13,7 +13,7 @@ usage() { 说明: - 不带参数时,默认更新 stable 分支 - 带参数时,可以切换到指定分支或 tag - - 更新后会自动执行一次 ./backup.sh --dry-run 自检 + - 更新后会自动执行一次 ./backup.sh --self-check 自检 USAGE } @@ -53,4 +53,4 @@ chmod 755 \ "${SCRIPT_DIR}/install.sh" \ "${SCRIPT_DIR}/update.sh" -bash "${SCRIPT_DIR}/backup.sh" --dry-run +bash "${SCRIPT_DIR}/backup.sh" --self-check