380 行
14 KiB
Markdown
380 行
14 KiB
Markdown
# backup.sh
|
||
|
||
把指定目录打包成 `tar.gz`,再上传到远端:当前支持 **SMB / Samba** 与 **rclone**(覆盖 Google Drive / OneDrive / S3 / WebDAV 等所有 rclone 支持的远端)。同时支持 **Linux** 与 **macOS**。
|
||
|
||
> SFTP 等其它方式已在脚本中预留入口(`run_sftp`、`SFTP_*` 配置),目前未实现。
|
||
|
||
## 系统要求
|
||
|
||
- bash 3.2+ / tar / date / split
|
||
- `sha256sum`(Linux 自带,属于 coreutils)或 `shasum`(macOS 自带)
|
||
- 上传 SMB:`smbclient`(Linux 一般在 `smbclient` 或 `samba-client` 包中;macOS 必须 `brew install samba`,脚本不再回退到 `mount_smbfs`)
|
||
- 上传 rclone:`rclone`,且需提前用 `rclone config` 配好远端
|
||
|
||
### 安装依赖
|
||
|
||
| 系统 | SMB | rclone |
|
||
| --- | --- | --- |
|
||
| Debian / Ubuntu | `apt install -y smbclient` | `apt install -y rclone` 或官方脚本 |
|
||
| RHEL / Rocky / Alma | `dnf install -y samba-client` | `dnf install -y rclone` 或官方脚本 |
|
||
| Arch | `pacman -S smbclient` | `pacman -S rclone` |
|
||
| macOS | `brew install samba`(必需) | `brew install rclone` |
|
||
| 通用 | — | `curl https://rclone.org/install.sh \| sudo bash` |
|
||
|
||
## 文件
|
||
|
||
```
|
||
backup/
|
||
├── backup.sh # 主脚本
|
||
├── backup.conf # 配置文件(与脚本同目录)
|
||
└── README.md
|
||
```
|
||
|
||
## 下载到本地
|
||
|
||
为避免后续脚本更新导致 `bash <(curl ...)` 形式的执行失效(参数变化、行为不兼容等),建议先下载到本地再执行:
|
||
|
||
```bash
|
||
# 创建目录并下载脚本与示例配置
|
||
mkdir -p /opt/backup && cd /opt/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 /opt/backup/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 上传 |
|
||
| `rclone` | 通过 rclone 上传到网盘 / 对象存储等任意 rclone 支持的远端 |
|
||
| `sftp` | 已预留入口,暂未实现 |
|
||
|
||
最常见的用法是先编辑同目录下的 `backup.conf`,然后执行:
|
||
|
||
```bash
|
||
bash backup.sh smb
|
||
```
|
||
|
||
## 配置文件 `backup.conf`
|
||
|
||
脚本默认读取 **脚本所在目录** 下的 `backup.conf`,可用 `-C` / `--config` 指定其它路径。
|
||
|
||
配置分三段:**公共配置**(`COMMON_*`,所有方式都用)、**SMB 段**、**SFTP 段**。备份方式由命令行第一个位置参数决定(`smb` / `sftp`),脚本只会读取该方式对应段的配置。`backup.conf` 里没有 `METHOD` 字段。
|
||
|
||
### 公共配置(COMMON_*)
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `COMMON_SOURCE_PATHS` | 要备份的源路径,多个用空格分隔,需引号包裹 |
|
||
| `COMMON_TMP_DIR` | 本地临时打包目录,默认 `/tmp/backup_script` |
|
||
| `COMMON_CLEAN_LOCAL` 配套行为 | 上传完成或失败后,会清理 `${COMMON_TMP_DIR}/${COMMON_ARCHIVE_PREFIX}*` 匹配到的文件/目录(含上次失败遗留的同前缀产物),同目录下其它无关文件不受影响 |
|
||
| `COMMON_ARCHIVE_PREFIX` | 归档/远端目录命名前缀,最终形如 `prefix-YYYYmmdd-HHMMSS/` |
|
||
| `COMMON_CLEAN_LOCAL` | 上传后是否删除本地归档(`true` / `false`) |
|
||
| `COMMON_RETENTION_DAYS` | 远端保留天数,`0` 表示不清理;按目录整体清理 |
|
||
| `COMMON_SPLIT_SIZE` | 分卷大小(默认 `1G`;`500M` / `100k` 等),留空字符串不分卷 |
|
||
|
||
### SMB 段
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `SMB_HOST` | SMB 服务器地址 |
|
||
| `SMB_SHARE` | 共享名 |
|
||
| `SMB_PATH` | 共享内的子目录(可选) |
|
||
| `SMB_USER` / `SMB_PASSWORD` | 凭据 |
|
||
| `SMB_DOMAIN` | 域 / 工作组(可选) |
|
||
| `SMB_VERSION` | SMB 协议版本,如 `3.0`(可选) |
|
||
|
||
### rclone 段
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `RCLONE_EXECUTABLE` | rclone 可执行文件路径(可选,留空则从 `PATH` 中查找)。装在非标准路径时填这里 |
|
||
| `RCLONE_REMOTE` | rclone 远端名(**不带尾随冒号**),例如 `gdrive`。需提前用 `rclone config` 配好 |
|
||
| `RCLONE_PATH` | 远端目标子路径,例如 `vps-backup/web1` |
|
||
| `RCLONE_CONFIG` | 自定义 rclone.conf 路径(可选,留空则使用 `~/.config/rclone/rclone.conf`) |
|
||
| `RCLONE_FLAGS` | 透传给 `rclone` 的额外参数(可选),常用:`--bwlimit 10M --transfers 2 --tpslimit 4` |
|
||
|
||
> 网盘类远端(Google Drive、OneDrive 等)建议加 `--tpslimit` 限制每秒事务数,避免触发风控;带宽紧张的服务器加 `--bwlimit` 限速。
|
||
|
||
### SFTP 段(预留,暂未实现)
|
||
|
||
`SFTP_HOST` / `SFTP_PORT` / `SFTP_USER` / `SFTP_PASSWORD` / `SFTP_KEY` / `SFTP_PATH`
|
||
|
||
## 命令行参数
|
||
|
||
所有 `backup.conf` 中的字段均可通过命令行参数覆盖;命令行参数优先级最高。
|
||
|
||
### 通用
|
||
|
||
| 参数 | 对应配置 |
|
||
| --- | --- |
|
||
| `-C, --config FILE` | 指定配置文件路径 |
|
||
| `-s, --source "P1 P2"` | `COMMON_SOURCE_PATHS` |
|
||
| `-t, --tmp-dir DIR` | `COMMON_TMP_DIR` |
|
||
| `-p, --prefix NAME` | `COMMON_ARCHIVE_PREFIX` |
|
||
| `--keep-local` | 等价于 `COMMON_CLEAN_LOCAL=false` |
|
||
| `--retention DAYS` | `COMMON_RETENTION_DAYS` |
|
||
| `--split-size SIZE` | `COMMON_SPLIT_SIZE` |
|
||
| `--debug` | 打印详细调试日志(也可用 `DEBUG=true` 环境变量) |
|
||
| `-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` |
|
||
|
||
### rclone
|
||
|
||
| 参数 | 对应配置 |
|
||
| --- | --- |
|
||
| `--rclone-executable F` | `RCLONE_EXECUTABLE` |
|
||
| `--rclone-remote NAME` | `RCLONE_REMOTE` |
|
||
| `--rclone-path PATH` | `RCLONE_PATH` |
|
||
| `--rclone-config FILE` | `RCLONE_CONFIG` |
|
||
| `--rclone-flags STR` | `RCLONE_FLAGS`(整串透传,需引号包裹) |
|
||
|
||
### 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"
|
||
```
|
||
|
||
rclone 上传到 Google Drive(先 `rclone config` 配好名为 `gdrive` 的远端):
|
||
|
||
```bash
|
||
bash backup.sh rclone \
|
||
--rclone-remote gdrive \
|
||
--rclone-path vps-backup/web1 \
|
||
--rclone-flags "--bwlimit 10M --transfers 2 --tpslimit 4" \
|
||
--retention 30
|
||
```
|
||
|
||
完全依赖配置文件的 rclone 用法:
|
||
|
||
```bash
|
||
bash backup.sh rclone
|
||
```
|
||
|
||
### rclone 远端的初始化
|
||
|
||
脚本本身不做交互式 OAuth,需要先在本机跑一次 `rclone config`:
|
||
|
||
```bash
|
||
rclone config
|
||
# 选 n (new remote) -> 输入名字(如 gdrive)-> 选存储类型 -> 跟着提示走
|
||
rclone listremotes # 应能看到 "gdrive:"
|
||
rclone lsd gdrive: # 验证可访问
|
||
```
|
||
|
||
之后把同样的远端名填到 `backup.conf` 的 `RCLONE_REMOTE`(不带冒号)。
|
||
|
||
## 定时任务
|
||
|
||
使用 `>`(单箭头)覆盖写入日志,避免日志文件无限增长——只保留最近一次执行的日志:
|
||
|
||
```cron
|
||
# 每天 03:10 跑一次
|
||
10 3 * * * /bin/bash /opt/backup/backup.sh smb > /opt/backup/backup.log 2>&1
|
||
|
||
# 每天 03:17 跑一次
|
||
17 3 * * * /bin/bash /opt/backup/backup.sh smb > /opt/backup/backup.log 2>&1
|
||
|
||
# 每两天 03:10 跑一次(按月内的奇数日触发:1、3、5……29、31)
|
||
10 3 */2 * * /bin/bash /opt/backup/backup.sh smb > /opt/backup/backup.log 2>&1
|
||
|
||
# 每 5 天 03:10 跑一次(按月内日期号取模:1、6、11、16、21、26、31)
|
||
10 3 */5 * * /bin/bash /opt/backup/backup.sh smb > /opt/backup/backup.log 2>&1
|
||
```
|
||
|
||
> `*/N` 是按月内日期号取模,并不是严格意义的「每 N×24 小时」。月末跨月时会出现间隔被截短的情况(例如 `*/5` 在 31 号触发后,次月 1 号又会触发);如对间隔严格要求,建议改用 systemd timer 的 `OnUnitActiveSec=5d`。
|
||
|
||
> 如果有多条任务都写到同一个日志文件,请改用不同的日志路径(例如 `/opt/backup/backup-0310.log`、`/opt/backup/backup-0317.log`),否则后一次会覆盖前一次。
|
||
|
||
macOS 可用 `launchd` 或 `cron`(需要在「系统设置 → 隐私与安全性 → 完全磁盘访问权限」中授予 `cron` 权限以读取受保护目录)。
|
||
|
||
### 立即执行一次验证
|
||
|
||
写完 cron 后建议先手动跑一遍验证,避免等到凌晨才发现配置问题(密码、网络、目录权限等)。
|
||
|
||
**方法 A**:直接照搬 cron 那行命令在终端跑一次,看日志是否完整:
|
||
|
||
```bash
|
||
/bin/bash /opt/backup/backup.sh smb > /opt/backup/backup.log 2>&1
|
||
tail -n 80 /opt/backup/backup.log
|
||
```
|
||
|
||
**方法 B**:用 cron 自身的环境跑一次(更接近真实定时执行的环境,能复现 PATH / 语言区域等差异)。临时把 cron 改成「下一分钟」触发:
|
||
|
||
```bash
|
||
# 假设当前时间 21:34,把 cron 改成 21:35 跑一次
|
||
crontab -e
|
||
# 临时改成: 35 21 * * * /bin/bash /opt/backup/backup.sh smb > /opt/backup/backup.log 2>&1
|
||
# 等一分钟后:
|
||
tail -f /opt/backup/backup.log
|
||
# 验证通过后再 crontab -e 恢复成原计划(例如 10 3 * * *)
|
||
```
|
||
|
||
**方法 C**(最干脆):debug 模式直接前台跑,进度全在终端:
|
||
|
||
```bash
|
||
bash /opt/backup/backup.sh smb --debug
|
||
```
|
||
|
||
## 远端目录结构
|
||
|
||
每次备份在远端创建一个独立子目录(SMB 与 rclone 行为一致),内含分卷文件与 SHA256 清单:
|
||
|
||
```
|
||
# SMB
|
||
//${SMB_HOST}/${SMB_SHARE}/${SMB_PATH}/
|
||
└── backup-20260426-031000/ # 本次备份目录
|
||
├── backup-20260426-031000.tar.gz.0 # 分卷 0
|
||
├── backup-20260426-031000.tar.gz.1 # 分卷 1
|
||
├── backup-20260426-031000.tar.gz.2
|
||
├── ...
|
||
└── backup-20260426-031000.sha256 # SHA256 清单(覆盖所有分卷)
|
||
|
||
# rclone
|
||
${RCLONE_REMOTE}:${RCLONE_PATH}/
|
||
└── backup-20260426-031000/
|
||
├── backup-20260426-031000.tar.gz.0
|
||
├── ...
|
||
└── backup-20260426-031000.sha256
|
||
```
|
||
|
||
未启用分卷(`COMMON_SPLIT_SIZE=""`)时目录里只有完整归档加清单:
|
||
|
||
```
|
||
backup-20260426-031000/
|
||
├── backup-20260426-031000.tar.gz
|
||
└── backup-20260426-031000.sha256
|
||
```
|
||
|
||
本地路径:
|
||
|
||
| 路径 | 说明 |
|
||
| --- | --- |
|
||
| `${COMMON_TMP_DIR}/${COMMON_ARCHIVE_PREFIX}-YYYYmmdd-HHMMSS.tar.gz` | 未分卷时的归档;分卷后会被删除 |
|
||
| `${COMMON_TMP_DIR}/${COMMON_ARCHIVE_PREFIX}-YYYYmmdd-HHMMSS.tar.gz.0` ... | 分卷文件(默认上传后删除) |
|
||
| `${COMMON_TMP_DIR}/${COMMON_ARCHIVE_PREFIX}-YYYYmmdd-HHMMSS.sha256` | 校验清单 |
|
||
|
||
## 分卷(COMMON_SPLIT_SIZE)
|
||
|
||
当单个归档过大、SMB 上传容易中途失败、或目标文件系统有单文件大小限制时启用分卷。**默认 `1G`。**
|
||
|
||
```bash
|
||
# 配置文件
|
||
COMMON_SPLIT_SIZE="1G"
|
||
|
||
# 或命令行
|
||
bash backup.sh smb --split-size 500M
|
||
|
||
# 关闭分卷
|
||
bash backup.sh smb --split-size ''
|
||
```
|
||
|
||
注意:
|
||
|
||
- 分卷需要本地临时空间约为「归档体积 × 2」(先生成完整 `.tar.gz`,再 split)。磁盘紧张时把 `COMMON_TMP_DIR` 指到大盘。
|
||
- 远端清理(`COMMON_RETENTION_DAYS`)按 **目录** 清理:以 `${COMMON_ARCHIVE_PREFIX}-YYYYmmdd-HHMMSS` 命名的目录里所有内容(含 sha256)会一并删除。
|
||
|
||
## 校验与恢复
|
||
|
||
每次备份都会生成 `<basename>.sha256` 清单(标准 `sha256sum` 格式:`<hash> <filename>`),命名都用相对文件名,因此**恢复时进入备份目录直接校验即可**。
|
||
|
||
进入备份目录:
|
||
|
||
```bash
|
||
cd /path/to/backup-20260426-031000/
|
||
```
|
||
|
||
校验完整性:
|
||
|
||
```bash
|
||
# Linux
|
||
sha256sum -c backup-20260426-031000.sha256
|
||
|
||
# macOS
|
||
shasum -a 256 -c backup-20260426-031000.sha256
|
||
```
|
||
|
||
合并分卷并解压(一步到位):
|
||
|
||
```bash
|
||
cat $(ls *.tar.gz.* | sort -V) | tar -xzf -
|
||
```
|
||
|
||
> `sort -V` 是 version-sort(自然数序),可正确排序 `.tar.gz.0 .tar.gz.1 ... .tar.gz.10 .tar.gz.11`。
|
||
> 直接 `cat *.tar.gz.*` 走的是 shell 字典序,会把 `.10` 排在 `.2` 前面,**会损坏归档**。
|
||
|
||
如果只有一个文件(未分卷):
|
||
|
||
```bash
|
||
tar -xzf backup-20260426-031000.tar.gz
|
||
```
|
||
|
||
或者先合并成一个文件再解压:
|
||
|
||
```bash
|
||
cat $(ls *.tar.gz.* | sort -V) > backup-20260426-031000.tar.gz
|
||
tar -xzf backup-20260426-031000.tar.gz
|
||
```
|
||
|
||
## 注意事项
|
||
|
||
- 命令行密码会出现在进程列表中,安全敏感场景请优先使用 `backup.conf`,并把权限收紧:`chmod 600 backup.conf`。
|
||
- macOS 上必须安装 `smbclient`(`brew install samba`),脚本不再使用 `mount_smbfs` 回退(旧行为不支持远端清理且语义与 Linux 不一致)。
|
||
- 远端清理仅清理符合 `${COMMON_ARCHIVE_PREFIX}-YYYYmmdd-HHMMSS` 命名规范的目录,避免误删其它内容。
|
||
- rclone 远端必须先在本机用 `rclone config` 配好;自定义 `RCLONE_CONFIG` 路径需保证脚本运行用户可读。
|
||
- 脚本不在上传后做远端 SHA256 校验(不同后端对 hash 的支持差异太大)。如需校验,恢复时进入备份目录用 `sha256sum -c` 对照同目录下的 `.sha256` 清单即可。
|