feat: 创建backup.sh
这个提交包含在:
179
backup/README.md
普通文件
179
backup/README.md
普通文件
@@ -0,0 +1,179 @@
|
|||||||
|
# backup.sh
|
||||||
|
|
||||||
|
把指定目录打包成 `tar.gz`,再通过 SMB / Samba 上传到远端服务器。同时支持 **Linux** 与 **macOS**。
|
||||||
|
|
||||||
|
> SFTP 等其它方式已在脚本中预留入口(`run_sftp`、`SFTP_*` 配置),目前未实现。
|
||||||
|
|
||||||
|
## 系统要求
|
||||||
|
|
||||||
|
- bash 3.2+ / tar / date
|
||||||
|
- Linux:`smbclient`(包含在 `smbclient` 或 `samba-client` 包中)
|
||||||
|
- macOS:`smbclient`(推荐,`brew install samba`),或退回到系统自带的 `mount_smbfs`
|
||||||
|
|
||||||
|
### 安装依赖
|
||||||
|
|
||||||
|
| 系统 | 命令 |
|
||||||
|
| --- | --- |
|
||||||
|
| Debian / Ubuntu | `apt install -y smbclient` |
|
||||||
|
| RHEL / Rocky / Alma | `dnf install -y samba-client` |
|
||||||
|
| Arch | `pacman -S smbclient` |
|
||||||
|
| macOS | `brew install samba`(可选,无则使用 `mount_smbfs`) |
|
||||||
|
|
||||||
|
## 文件
|
||||||
|
|
||||||
|
```
|
||||||
|
backup/
|
||||||
|
├── backup.sh # 主脚本
|
||||||
|
├── backup.conf # 配置文件(与脚本同目录)
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 下载到本地
|
||||||
|
|
||||||
|
为避免后续脚本更新导致 `bash <(curl ...)` 形式的执行失效(参数变化、行为不兼容等),建议先下载到本地再执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 创建目录并下载脚本与示例配置
|
||||||
|
mkdir -p ~/backup && cd ~/backup
|
||||||
|
curl -fsSL -o backup.sh https://git.suhang.me/suhang/scripts/raw/branch/release/backup/backup.sh
|
||||||
|
curl -fsSL -o backup.conf https://git.suhang.me/suhang/scripts/raw/branch/release/backup/backup.conf
|
||||||
|
chmod +x backup.sh
|
||||||
|
chmod 600 backup.conf
|
||||||
|
|
||||||
|
# 编辑 backup.conf 后执行
|
||||||
|
bash backup.sh smb
|
||||||
|
```
|
||||||
|
|
||||||
|
也可改用 `wget`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget -qO backup.sh https://git.suhang.me/suhang/scripts/raw/branch/release/backup/backup.sh
|
||||||
|
wget -qO backup.conf https://git.suhang.me/suhang/scripts/raw/branch/release/backup/backup.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash backup.sh <method> [options]
|
||||||
|
```
|
||||||
|
|
||||||
|
`method` 取值:
|
||||||
|
|
||||||
|
| 方式 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `smb` | 通过 SMB / Samba 上传 |
|
||||||
|
| `sftp` | 已预留入口,暂未实现 |
|
||||||
|
|
||||||
|
最常见的用法是先编辑同目录下的 `backup.conf`,然后执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash backup.sh smb
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置文件 `backup.conf`
|
||||||
|
|
||||||
|
脚本默认读取 **脚本所在目录** 下的 `backup.conf`,可用 `-C` / `--config` 指定其它路径。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `SOURCE_PATHS` | 要备份的源路径,多个用空格分隔,需引号包裹 |
|
||||||
|
| `TMP_DIR` | 本地临时打包目录,默认 `/tmp/backup` |
|
||||||
|
| `ARCHIVE_PREFIX` | 归档文件名前缀,最终形如 `prefix-YYYYmmdd-HHMMSS.tar.gz` |
|
||||||
|
| `METHOD` | 默认上传方式(`smb` / `sftp`),命令行参数会覆盖 |
|
||||||
|
| `CLEAN_LOCAL` | 上传后是否删除本地归档(`true` / `false`) |
|
||||||
|
| `RETENTION_DAYS` | 远端保留天数,`0` 表示不清理 |
|
||||||
|
| `SMB_HOST` | SMB 服务器地址 |
|
||||||
|
| `SMB_SHARE` | 共享名 |
|
||||||
|
| `SMB_PATH` | 共享内的子目录(可选) |
|
||||||
|
| `SMB_USER` / `SMB_PASSWORD` | 凭据 |
|
||||||
|
| `SMB_DOMAIN` | 域 / 工作组(可选) |
|
||||||
|
| `SMB_VERSION` | SMB 协议版本,如 `3.0`(可选) |
|
||||||
|
| `SFTP_*` | 预留字段,目前未启用 |
|
||||||
|
|
||||||
|
## 命令行参数
|
||||||
|
|
||||||
|
所有 `backup.conf` 中的字段均可通过命令行参数覆盖;命令行参数优先级最高。
|
||||||
|
|
||||||
|
### 通用
|
||||||
|
|
||||||
|
| 参数 | 对应配置 |
|
||||||
|
| --- | --- |
|
||||||
|
| `-C, --config FILE` | 指定配置文件路径 |
|
||||||
|
| `-s, --source "P1 P2"` | `SOURCE_PATHS` |
|
||||||
|
| `-t, --tmp-dir DIR` | `TMP_DIR` |
|
||||||
|
| `-p, --prefix NAME` | `ARCHIVE_PREFIX` |
|
||||||
|
| `--keep-local` | 等价于 `CLEAN_LOCAL=false` |
|
||||||
|
| `--retention DAYS` | `RETENTION_DAYS` |
|
||||||
|
| `-h, --help` | 显示帮助 |
|
||||||
|
|
||||||
|
### SMB
|
||||||
|
|
||||||
|
| 参数 | 对应配置 |
|
||||||
|
| --- | --- |
|
||||||
|
| `--smb-host HOST` | `SMB_HOST` |
|
||||||
|
| `--smb-share NAME` | `SMB_SHARE` |
|
||||||
|
| `--smb-path PATH` | `SMB_PATH` |
|
||||||
|
| `--smb-user USER` | `SMB_USER` |
|
||||||
|
| `--smb-password PASS` | `SMB_PASSWORD` |
|
||||||
|
| `--smb-domain DOMAIN` | `SMB_DOMAIN` |
|
||||||
|
| `--smb-version VER` | `SMB_VERSION` |
|
||||||
|
|
||||||
|
### SFTP(预留)
|
||||||
|
|
||||||
|
`--sftp-host` `--sftp-port` `--sftp-user` `--sftp-password` `--sftp-key` `--sftp-path`
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
完全依赖配置文件:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash backup.sh smb
|
||||||
|
```
|
||||||
|
|
||||||
|
只用命令行参数(不依赖 `backup.conf`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash backup.sh smb \
|
||||||
|
-s "/etc /var/log /home/user/data" \
|
||||||
|
--smb-host 192.168.1.10 \
|
||||||
|
--smb-share backup \
|
||||||
|
--smb-path servers/web1 \
|
||||||
|
--smb-user backup \
|
||||||
|
--smb-password 'P@ssw0rd' \
|
||||||
|
--retention 30
|
||||||
|
```
|
||||||
|
|
||||||
|
混合使用(基础信息读 `backup.conf`,源路径临时覆盖):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash backup.sh smb -s "/var/lib/mysql"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 定时任务
|
||||||
|
|
||||||
|
使用 `>`(单箭头)覆盖写入日志,避免日志文件无限增长——只保留最近一次执行的日志:
|
||||||
|
|
||||||
|
```cron
|
||||||
|
# 每天 03:10 跑一次
|
||||||
|
10 3 * * * /bin/bash /path/to/backup/backup.sh smb > /var/log/backup.log 2>&1
|
||||||
|
|
||||||
|
# 每天 03:17 跑一次
|
||||||
|
17 3 * * * /bin/bash /path/to/backup/backup.sh smb > /var/log/backup.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
> 如果有多条任务都写到同一个日志文件,请改用不同的日志路径(例如 `/var/log/backup-0310.log`、`/var/log/backup-0317.log`),否则后一次会覆盖前一次。
|
||||||
|
|
||||||
|
macOS 可用 `launchd` 或 `cron`(需要在「系统设置 → 隐私与安全性 → 完全磁盘访问权限」中授予 `cron` 权限以读取受保护目录)。
|
||||||
|
|
||||||
|
## 路径与文件
|
||||||
|
|
||||||
|
| 路径 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `${TMP_DIR}/${ARCHIVE_PREFIX}-YYYYmmdd-HHMMSS.tar.gz` | 本地临时归档(默认上传后删除) |
|
||||||
|
| 远端 `//${SMB_HOST}/${SMB_SHARE}/${SMB_PATH}/` | 上传目标目录 |
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- 命令行密码会出现在进程列表中,安全敏感场景请优先使用 `backup.conf`,并把权限收紧:`chmod 600 backup.conf`。
|
||||||
|
- macOS 上若未安装 `smbclient`,会回退到挂载方式(`mount_smbfs`),此时不支持 `--retention` 远端清理,仅完成上传。
|
||||||
|
- 远端清理仅清理符合 `${ARCHIVE_PREFIX}-YYYYmmdd-HHMMSS.tar.gz` 命名规范的文件,避免误删其它内容。
|
||||||
51
backup/backup.conf
普通文件
51
backup/backup.conf
普通文件
@@ -0,0 +1,51 @@
|
|||||||
|
# backup.sh 配置文件
|
||||||
|
# 所有配置项均可通过命令行参数覆盖(参数优先级高于本文件)
|
||||||
|
|
||||||
|
# ===== 通用配置 =====
|
||||||
|
|
||||||
|
# 要备份的源目录或文件(多个用空格分隔,需引号包裹)
|
||||||
|
SOURCE_PATHS="/etc /var/log"
|
||||||
|
|
||||||
|
# 本地临时打包目录(用于存放归档文件,备份完成后会清理)
|
||||||
|
TMP_DIR="/tmp/backup"
|
||||||
|
|
||||||
|
# 归档文件名前缀,最终文件名形如 ${ARCHIVE_PREFIX}-YYYYmmdd-HHMMSS.tar.gz
|
||||||
|
ARCHIVE_PREFIX="backup"
|
||||||
|
|
||||||
|
# 备份方式:smb / sftp(sftp 暂未实现)
|
||||||
|
METHOD="smb"
|
||||||
|
|
||||||
|
# 上传完成后是否删除本地归档(true / false)
|
||||||
|
CLEAN_LOCAL="true"
|
||||||
|
|
||||||
|
# 远端保留天数,0 表示不清理
|
||||||
|
RETENTION_DAYS=15
|
||||||
|
|
||||||
|
# ===== SMB / Samba 配置 =====
|
||||||
|
|
||||||
|
# 服务器地址,例如 192.168.1.10 或 nas.local
|
||||||
|
SMB_HOST=""
|
||||||
|
|
||||||
|
# 共享名,例如 backup
|
||||||
|
SMB_SHARE=""
|
||||||
|
|
||||||
|
# 共享内的子目录(可选),例如 servers/web1
|
||||||
|
SMB_PATH=""
|
||||||
|
|
||||||
|
# 用户名 / 密码
|
||||||
|
SMB_USER=""
|
||||||
|
SMB_PASSWORD=""
|
||||||
|
|
||||||
|
# 域 / 工作组(可选)
|
||||||
|
SMB_DOMAIN=""
|
||||||
|
|
||||||
|
# SMB 协议版本(可选),例如 3.0
|
||||||
|
SMB_VERSION=""
|
||||||
|
|
||||||
|
# ===== SFTP 配置(预留,暂未实现)=====
|
||||||
|
SFTP_HOST=""
|
||||||
|
SFTP_PORT="22"
|
||||||
|
SFTP_USER=""
|
||||||
|
SFTP_PASSWORD=""
|
||||||
|
SFTP_KEY=""
|
||||||
|
SFTP_PATH=""
|
||||||
345
backup/backup.sh
普通文件
345
backup/backup.sh
普通文件
@@ -0,0 +1,345 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# backup.sh — 打包源目录并通过 SMB / (预留) SFTP 上传到远端
|
||||||
|
# 同时支持 Linux 与 macOS
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
log() { printf "${CYAN}[%s]${NC} %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; }
|
||||||
|
warn() { printf "${YELLOW}[%s] WARN:${NC} %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; }
|
||||||
|
err() { printf "${RED}[%s] ERROR:${NC} %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >&2; }
|
||||||
|
ok() { printf "${GREEN}[%s]${NC} %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; }
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
CONF_FILE="${SCRIPT_DIR}/backup.conf"
|
||||||
|
|
||||||
|
# ---- 默认值 ----
|
||||||
|
SOURCE_PATHS=""
|
||||||
|
TMP_DIR="/tmp/backup"
|
||||||
|
ARCHIVE_PREFIX="backup"
|
||||||
|
METHOD=""
|
||||||
|
CLEAN_LOCAL="true"
|
||||||
|
RETENTION_DAYS=0
|
||||||
|
|
||||||
|
SMB_HOST=""
|
||||||
|
SMB_SHARE=""
|
||||||
|
SMB_PATH=""
|
||||||
|
SMB_USER=""
|
||||||
|
SMB_PASSWORD=""
|
||||||
|
SMB_DOMAIN=""
|
||||||
|
SMB_VERSION=""
|
||||||
|
|
||||||
|
SFTP_HOST=""
|
||||||
|
SFTP_PORT="22"
|
||||||
|
SFTP_USER=""
|
||||||
|
SFTP_PASSWORD=""
|
||||||
|
SFTP_KEY=""
|
||||||
|
SFTP_PATH=""
|
||||||
|
|
||||||
|
OS_NAME="$(uname -s)"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: bash backup.sh <method> [options]
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
smb 通过 SMB / Samba 协议上传
|
||||||
|
sftp 通过 SFTP 上传(暂未实现,已预留入口)
|
||||||
|
|
||||||
|
Common options:
|
||||||
|
-C, --config FILE 指定配置文件路径(默认:脚本所在目录下 backup.conf)
|
||||||
|
-s, --source PATHS 要备份的源路径,多个用空格分隔,需引号包裹
|
||||||
|
-t, --tmp-dir DIR 本地临时目录(默认:/tmp/backup)
|
||||||
|
-p, --prefix NAME 归档文件名前缀(默认:backup)
|
||||||
|
--keep-local 上传后保留本地归档
|
||||||
|
--retention DAYS 远端保留天数,0 表示不清理
|
||||||
|
-h, --help 显示帮助
|
||||||
|
|
||||||
|
SMB options:
|
||||||
|
--smb-host HOST
|
||||||
|
--smb-share NAME
|
||||||
|
--smb-path PATH 共享内子目录
|
||||||
|
--smb-user USER
|
||||||
|
--smb-password PASS
|
||||||
|
--smb-domain DOMAIN
|
||||||
|
--smb-version VER 例如 3.0
|
||||||
|
|
||||||
|
SFTP options (预留):
|
||||||
|
--sftp-host HOST
|
||||||
|
--sftp-port PORT
|
||||||
|
--sftp-user USER
|
||||||
|
--sftp-password PASS
|
||||||
|
--sftp-key FILE
|
||||||
|
--sftp-path PATH
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
bash backup.sh smb
|
||||||
|
bash backup.sh smb --smb-host 192.168.1.10 --smb-share backup --smb-user u --smb-password p
|
||||||
|
bash backup.sh smb -s "/etc /var/log" -C /path/to/backup.conf
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
load_conf() {
|
||||||
|
if [[ -f "$CONF_FILE" ]]; then
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$CONF_FILE"
|
||||||
|
log "已加载配置文件:$CONF_FILE"
|
||||||
|
else
|
||||||
|
warn "配置文件不存在:$CONF_FILE(仅使用命令行参数)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args() {
|
||||||
|
if [[ $# -lt 1 ]]; then
|
||||||
|
usage; exit 1
|
||||||
|
fi
|
||||||
|
case "$1" in
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
smb|sftp) METHOD_ARG="$1"; shift ;;
|
||||||
|
*) err "未知方式:$1"; usage; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# 第一遍只取 --config,使后续参数能覆盖配置
|
||||||
|
local i=1
|
||||||
|
local argv=("$@")
|
||||||
|
while [[ $i -le ${#argv[@]} ]]; do
|
||||||
|
local cur="${argv[$((i-1))]}"
|
||||||
|
if [[ "$cur" == "-C" || "$cur" == "--config" ]]; then
|
||||||
|
CONF_FILE="${argv[$i]}"
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
|
||||||
|
load_conf
|
||||||
|
METHOD="$METHOD_ARG"
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-C|--config) shift 2 ;; # 已处理
|
||||||
|
-s|--source) SOURCE_PATHS="$2"; shift 2 ;;
|
||||||
|
-t|--tmp-dir) TMP_DIR="$2"; shift 2 ;;
|
||||||
|
-p|--prefix) ARCHIVE_PREFIX="$2"; shift 2 ;;
|
||||||
|
--keep-local) CLEAN_LOCAL="false"; shift ;;
|
||||||
|
--retention) RETENTION_DAYS="$2"; shift 2 ;;
|
||||||
|
--smb-host) SMB_HOST="$2"; shift 2 ;;
|
||||||
|
--smb-share) SMB_SHARE="$2"; shift 2 ;;
|
||||||
|
--smb-path) SMB_PATH="$2"; shift 2 ;;
|
||||||
|
--smb-user) SMB_USER="$2"; shift 2 ;;
|
||||||
|
--smb-password) SMB_PASSWORD="$2"; shift 2 ;;
|
||||||
|
--smb-domain) SMB_DOMAIN="$2"; shift 2 ;;
|
||||||
|
--smb-version) SMB_VERSION="$2"; shift 2 ;;
|
||||||
|
--sftp-host) SFTP_HOST="$2"; shift 2 ;;
|
||||||
|
--sftp-port) SFTP_PORT="$2"; shift 2 ;;
|
||||||
|
--sftp-user) SFTP_USER="$2"; shift 2 ;;
|
||||||
|
--sftp-password) SFTP_PASSWORD="$2"; shift 2 ;;
|
||||||
|
--sftp-key) SFTP_KEY="$2"; shift 2 ;;
|
||||||
|
--sftp-path) SFTP_PATH="$2"; shift 2 ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) err "未知参数:$1"; usage; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
require_cmd() {
|
||||||
|
for c in "$@"; do
|
||||||
|
if ! command -v "$c" >/dev/null 2>&1; then
|
||||||
|
err "缺少依赖命令:$c"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
create_archive() {
|
||||||
|
[[ -z "$SOURCE_PATHS" ]] && { err "未配置 SOURCE_PATHS"; return 1; }
|
||||||
|
|
||||||
|
mkdir -p "$TMP_DIR" || { err "无法创建临时目录:$TMP_DIR"; return 1; }
|
||||||
|
|
||||||
|
local ts archive
|
||||||
|
ts="$(date '+%Y%m%d-%H%M%S')"
|
||||||
|
archive="${TMP_DIR}/${ARCHIVE_PREFIX}-${ts}.tar.gz"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2206
|
||||||
|
local paths=($SOURCE_PATHS)
|
||||||
|
for p in "${paths[@]}"; do
|
||||||
|
if [[ ! -e "$p" ]]; then
|
||||||
|
warn "源路径不存在,跳过:$p"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log "开始打包:$archive"
|
||||||
|
if ! tar -czf "$archive" "${paths[@]}" 2>/dev/null; then
|
||||||
|
err "打包失败"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
ARCHIVE_FILE="$archive"
|
||||||
|
ARCHIVE_NAME="$(basename "$archive")"
|
||||||
|
ok "打包完成:$archive ($(du -h "$archive" | awk '{print $1}'))"
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_local() {
|
||||||
|
if [[ "$CLEAN_LOCAL" == "true" && -n "${ARCHIVE_FILE:-}" && -f "$ARCHIVE_FILE" ]]; then
|
||||||
|
rm -f "$ARCHIVE_FILE" && log "已删除本地归档:$ARCHIVE_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------- SMB ----------
|
||||||
|
|
||||||
|
smb_check_deps() {
|
||||||
|
case "$OS_NAME" in
|
||||||
|
Linux)
|
||||||
|
if command -v smbclient >/dev/null 2>&1; then
|
||||||
|
SMB_TOOL="smbclient"
|
||||||
|
else
|
||||||
|
err "未安装 smbclient。Debian/Ubuntu: apt install smbclient;RHEL/Alma: dnf install samba-client"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
Darwin)
|
||||||
|
# macOS 自带 smbutil,但上传需要挂载;优先使用 smbclient(brew install samba)
|
||||||
|
if command -v smbclient >/dev/null 2>&1; then
|
||||||
|
SMB_TOOL="smbclient"
|
||||||
|
elif command -v mount_smbfs >/dev/null 2>&1; then
|
||||||
|
SMB_TOOL="mount_smbfs"
|
||||||
|
else
|
||||||
|
err "未找到 smbclient(brew install samba)或 mount_smbfs"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*) err "不支持的系统:$OS_NAME"; return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
smb_validate() {
|
||||||
|
[[ -z "$SMB_HOST" ]] && { err "缺少 SMB_HOST"; return 1; }
|
||||||
|
[[ -z "$SMB_SHARE" ]] && { err "缺少 SMB_SHARE"; return 1; }
|
||||||
|
[[ -z "$SMB_USER" ]] && { err "缺少 SMB_USER"; return 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
smb_upload_smbclient() {
|
||||||
|
local remote_dir="${SMB_PATH%/}"
|
||||||
|
local commands=""
|
||||||
|
|
||||||
|
if [[ -n "$remote_dir" ]]; then
|
||||||
|
# 逐级 mkdir,避免目录不存在
|
||||||
|
local IFS=/
|
||||||
|
# shellcheck disable=SC2206
|
||||||
|
local parts=($remote_dir)
|
||||||
|
local cur=""
|
||||||
|
for seg in "${parts[@]}"; do
|
||||||
|
[[ -z "$seg" ]] && continue
|
||||||
|
cur="${cur}${seg}"
|
||||||
|
commands+="mkdir \"${cur}\";"
|
||||||
|
cur="${cur}/"
|
||||||
|
done
|
||||||
|
commands+="cd \"${remote_dir}\";"
|
||||||
|
fi
|
||||||
|
commands+="put \"${ARCHIVE_FILE}\" \"${ARCHIVE_NAME}\";"
|
||||||
|
|
||||||
|
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//./}" )
|
||||||
|
|
||||||
|
log "通过 smbclient 上传到 //${SMB_HOST}/${SMB_SHARE}/${SMB_PATH}"
|
||||||
|
if ! smbclient "${args[@]}" -c "$commands"; then
|
||||||
|
err "smbclient 上传失败"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$RETENTION_DAYS" -gt 0 ]]; then
|
||||||
|
smb_retention_smbclient "${args[@]}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
smb_retention_smbclient() {
|
||||||
|
local args=( "$@" )
|
||||||
|
local listing
|
||||||
|
listing="$(smbclient "${args[@]}" -D "${SMB_PATH:-/}" -c "ls" 2>/dev/null)" || return 0
|
||||||
|
local cutoff
|
||||||
|
if date -v-1d >/dev/null 2>&1; then
|
||||||
|
cutoff=$(date -v-"${RETENTION_DAYS}"d +%s) # macOS BSD date
|
||||||
|
else
|
||||||
|
cutoff=$(date -d "-${RETENTION_DAYS} days" +%s)
|
||||||
|
fi
|
||||||
|
log "清理远端早于 ${RETENTION_DAYS} 天的归档"
|
||||||
|
echo "$listing" | awk '{print $1}' | grep -E "^${ARCHIVE_PREFIX}-[0-9]{8}-[0-9]{6}\\.tar\\.gz\$" | while read -r f; do
|
||||||
|
local fts ftime
|
||||||
|
fts="$(echo "$f" | sed -E "s/^${ARCHIVE_PREFIX}-([0-9]{8})-([0-9]{6})\\.tar\\.gz\$/\\1 \\2/")"
|
||||||
|
local d="${fts% *}" t="${fts#* }"
|
||||||
|
local iso="${d:0:4}-${d:4:2}-${d:6:2} ${t:0:2}:${t:2:2}:${t:4:2}"
|
||||||
|
if date -j -f "%Y-%m-%d %H:%M:%S" "$iso" +%s >/dev/null 2>&1; then
|
||||||
|
ftime=$(date -j -f "%Y-%m-%d %H:%M:%S" "$iso" +%s) # macOS
|
||||||
|
else
|
||||||
|
ftime=$(date -d "$iso" +%s)
|
||||||
|
fi
|
||||||
|
if [[ "$ftime" -lt "$cutoff" ]]; then
|
||||||
|
log "删除远端旧归档:$f"
|
||||||
|
smbclient "${args[@]}" -D "${SMB_PATH:-/}" -c "del \"$f\"" >/dev/null 2>&1 || warn "删除失败:$f"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
smb_upload_mount() {
|
||||||
|
# macOS 兜底:通过挂载点拷贝
|
||||||
|
local mnt
|
||||||
|
mnt="$(mktemp -d /tmp/smbmnt.XXXXXX)"
|
||||||
|
local url="//${SMB_USER}:${SMB_PASSWORD}@${SMB_HOST}/${SMB_SHARE}"
|
||||||
|
log "挂载 SMB:${mnt}"
|
||||||
|
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"
|
||||||
|
mkdir -p "$dest" || true
|
||||||
|
fi
|
||||||
|
if cp "$ARCHIVE_FILE" "$dest/"; then
|
||||||
|
ok "已复制到 $dest/"
|
||||||
|
else
|
||||||
|
err "复制失败"
|
||||||
|
umount "$mnt" 2>/dev/null
|
||||||
|
rmdir "$mnt"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
umount "$mnt" 2>/dev/null
|
||||||
|
rmdir "$mnt"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_smb() {
|
||||||
|
smb_check_deps || return 1
|
||||||
|
smb_validate || return 1
|
||||||
|
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
|
||||||
|
ok "SMB 上传完成"
|
||||||
|
cleanup_local
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------- SFTP(预留)----------
|
||||||
|
|
||||||
|
run_sftp() {
|
||||||
|
err "SFTP 方式尚未实现,已预留入口(参见 run_sftp 函数与 SFTP_* 配置)"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------- 入口 ----------
|
||||||
|
|
||||||
|
main() {
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
case "$METHOD" in
|
||||||
|
smb) run_smb ;;
|
||||||
|
sftp) run_sftp ;;
|
||||||
|
*) err "不支持的方式:$METHOD"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
在新工单中引用
屏蔽一个用户