U盘即插备份

想要插上 U 盘就自动把重大文件备份到服务器/ NAS?别再手动复制了——本篇教大家在 Linux 上用 udev + systemd + rsync 实现「插入即备份」:零交互、日志可查、安全可控。步骤清晰、命令可复制粘贴,适合家庭服务器或工作站自动化备份小方案。

一、方案概览(用一句话)

当系统检测到 USB 存储设备插入 时,udev 触发 systemd 模板服务,调用备份脚本把 U 盘内容增量 rsync 到服务器上的备份目录,并记录日志与状态。

U盘即插备份

二、适用场景与准备

适合:Linux 桌面、家庭 NAS、个人服务器(Ubuntu/Debian/CentOS/Alma 等)

必备:

  • rsync、blkid、lsblk、mount(大多数发行版已自带)
  • root 权限(写入 /etc/udev、/etc/systemd/system)
  • 一个备份目标目录(例如 /srv/usb-backups 或挂载的网络磁盘 /mnt/backup)
  • 推荐:为备份主机设置足够磁盘空间并定期清理

U盘即插备份

三、实现流程(文件会放在这些位置)

  • 备份脚本:/usr/local/bin/usb-backup.sh
  • systemd 模板单元:/etc/systemd/system/usb-backup@.service
  • udev 规则:/etc/udev/rules.d/99-usb-backup.rules
  • 日志查看:journalctl -u usb-backup@<DEV>.service

四、一步步部署(复制粘贴即可)

1)创建备份目标目录并设置权限

把备份放在服务器本地目录或已挂载的网络盘(例如 /mnt/backup)。示例用 /srv/usb-backups:

sudo mkdir -p /srv/usb-backups

sudo chown –reference=/root /srv/usb-backups # 可改为某个非 root 用户

sudo chmod 750 /srv/usb-backups

2)写备份脚本

/usr/local/bin/usb-backup.sh

把下面脚本保存为
/usr/local/bin/usb-backup.sh,并 chmod +x。脚本做事:拿到设备(例如 /dev/sdb1),读取 LABEL/UUID,临时挂载到 /tmp/usb-mount-<dev>.<ts>,用 rsync -aHAX –delete 增量同步到目标目录下以 LABEL || UUID || devname 命名的子目录,最后卸载并记录日志。脚本对异常做超时保护与返回码。

#!/usr/bin/env bash

# /usr/local/bin/usb-backup.sh

set -euo pipefail

DEVNAME=”$1″ # expect e.g. sdb1 (systemd will pass instance)

DEV=”/dev/${DEVNAME}”

LOGPREFIX=”[usb-backup ${DEV}]”

# Config

BACKUP_ROOT=”/srv/usb-backups”

MOUNT_BASE=”/tmp/usb-mount”

RSYNC_OPTS=(-aHAX –delete –info=progress2 –exclude 'lost+found' –partial –partial-dir=.rsync-partial)

# Timeout for mount/rsync operations (seconds)

OP_TIMEOUT=600

# Helper: get filesystem label or uuid fallback

get_label_or_uuid(){

local dev=”$1″

local label uuid

label=$(blkid -s LABEL -o value “$dev” 2>/dev/null || true)

uuid=$(blkid -s UUID -o value “$dev” 2>/dev/null || true)

if [[ -n “$label” ]]; then

echo “${label}”

elif [[ -n “$uuid” ]]; then

echo “${uuid}”

else

# fallback to device name

echo “${dev##*/}”

fi

}

# Ensure device exists and is a block device

if [[ ! -b “$DEV” ]]; then

echo “$LOGPREFIX device $DEV not found or not a block device” >&2

exit 2

fi

# Only handle removable USB filesystem partitions with type

id_bus=$(udevadm info –query=property –name=”$DEV” | awk -F= '/ID_BUS=/{print $2}')

id_fs_type=$(udevadm info –query=property –name=”$DEV” | awk -F= '/ID_FS_TYPE=/{print $2}')

if [[ “$id_bus” != “usb” ]] || [[ -z “$id_fs_type” ]]; then

echo “$LOGPREFIX skip device: bus=$id_bus fs_type=$id_fs_type” >&2

exit 3

fi

LABEL=$(get_label_or_uuid “$DEV”)

TS=$(date +%F_%H%M%S)

MOUNTPOINT=”${MOUNT_BASE}/${DEVNAME}_${TS}”

BACKUP_TARGET=”${BACKUP_ROOT}/${LABEL}_${TS}”

mkdir -p “$MOUNTPOINT”

chown root:root “$MOUNTPOINT”

chmod 700 “$MOUNTPOINT”

echo “$LOGPREFIX Mounting $DEV to $MOUNTPOINT”

# mount with timeout

if ! timeout “${OP_TIMEOUT}” mount -o ro,noexec,nodev,nosuid “$DEV” “$MOUNTPOINT”; then

echo “$LOGPREFIX Mount failed or timeout” >&2

rmdir “$MOUNTPOINT” 2>/dev/null || true

exit 4

fi

echo “$LOGPREFIX Starting rsync to $BACKUP_TARGET”

mkdir -p “$BACKUP_TARGET”

# Run rsync with timeout to avoid hung jobs

if ! timeout “${OP_TIMEOUT}” rsync “${RSYNC_OPTS[@]}” “$MOUNTPOINT/” “$BACKUP_TARGET/” ; then

echo “$LOGPREFIX rsync failed or timeout” >&2

# attempt umount

umount “$MOUNTPOINT” 2>/dev/null || true

rmdir “$MOUNTPOINT” 2>/dev/null || true

exit 5

fi

echo “$LOGPREFIX Sync complete. Unmounting”

umount “$MOUNTPOINT” || { echo “$LOGPREFIX umount failed, trying lazy umount”; umount -l “$MOUNTPOINT” || true; }

rmdir “$MOUNTPOINT” 2>/dev/null || true

# Optional: create a 'latest' symlink for easy access

ln -sfn “$BACKUP_TARGET” “${BACKUP_ROOT}/${LABEL}_latest”

echo “$LOGPREFIX Done. Backup at $BACKUP_TARGET”

exit 0

保存后:

sudo chmod +x /usr/local/bin/usb-backup.sh

说明:脚本以只读方式挂载 U 盘(-o ro)以防误写;rsync 用 –delete 做镜像(如需保留旧文件请去掉 –delete 或调整策略)。

3)创建 systemd 模板单元

/etc/systemd/system/usb-backup@.service

把下面文件写入,然后 reload systemd:

[Unit]

Description=USB Backup for %I

Requires=dev-%i.device

After=dev-%i.device

[Service]

Type=oneshot

# Pass instance name like sdb1; script expects device name without /dev/

ExecStart=/usr/local/bin/usb-backup.sh %i

TimeoutStartSec=900

Nice=10

# avoid running multiple simultaneous backups for same device

[Install]

WantedBy=multi-user.target

保存后:

sudo systemctl daemon-reload

4)写 udev 规则

/etc/udev/rules.d/99-usb-backup.rules

规则要筛选出 USB 总线且为分区且带文件系统 的情况,并使用 ENV{SYSTEMD_WANTS} 启动 systemd 模板服务(实例名是设备名如 sdb1):

# /etc/udev/rules.d/99-usb-backup.rules

# Trigger systemd service usb-backup@<dev>.service on USB partition add

SUBSYSTEM==”block”, ACTION==”add”, ENV{ID_BUS}==”usb”, ENV{ID_FS_TYPE}!=””, KERNEL==”sd?1″, ENV{SYSTEMD_WANTS}=”usb-backup@%k.service”

说明:

  • KERNEL==”sd?1″:只匹配分区(如 sdb1),避免在原始盘(sdb)上触发。根据你的设备可能需要调整(列如 sd[a-z][0-9]*)。
  • 若你的设备分区编号不同或多分区可调整 KERNEL==”sd*[0-9]*”。

写好后重载 udev:

sudo udevadm control –reload

sudo udevadm trigger –action=add

五、测试流程(安全步骤)

  1. 插入 U 盘(系统会识别分区,列如 /dev/sdb1)。
  2. 查看是否启动了服务:

sudo journalctl -f

# or

sudo systemctl status usb-backup@sdb1.service

  1. 日志里会显示挂载、rsync 进度与结果。也可查看 /srv/usb-backups/ 是否生成新目录。
  2. 若要人工触发(不插盘),可以直接运行:

sudo /usr/local/bin/usb-backup.sh sdb1

# 或用 systemd 启动同样的单元(假设 /dev/sdb1 存在)

sudo systemctl start usb-backup@sdb1.service

六、常见问题与排查(快速查表)

  • 服务未触发?
    • udevadm monitor –environment –udev 插入设备观察 udev 事件。
    • udevadm info –query=all –name=/dev/sdb1 查看 udev 属性(是否有 ID_BUS=usb 与 ID_FS_TYPE)。
    • sudo udevadm test /sys/block/sdb/sdb1(或对应 sys path)查看规则匹配情况。
  • rsync 挂起或超时?
    • 看 journalctl -u usb-backup@sdb1.service 日志,增加 OP_TIMEOUT 值或检查网络备份目标(若是远程挂载)。
  • 权限问题(备份目标不可写)?
    • 确认 /srv/usb-backups 的拥有者/权限允许 systemd 服务写入(默认 root 运行)。

  • 想对某些 U 盘跳过备份?

    • 在 udev 规则里加条件:例如 ENV{ID_SERIAL}!=”YourSerialHere” 或在脚本中检测 LABEL/UUID 后退出。

七、扩展功能(选配)

  • 自动解密/解锁 LUKS 分区:脚本可先尝试 cryptsetup 解锁(谨慎,需交互或 keyfile)。
  • 备份到远程:把 BACKUP_ROOT 指向已挂载的 NFS/SMB 或直接用 rsync 推到远端 rsync:///SSH(记得用 keyauth)。
  • 仅备份特定目录:在脚本里用 include/exclude 控制 rsync(如只备 Documents 目录)。
  • 排队/去重:若多盘频繁插入,改用 systemd 服务配合锁文件避免并发写入到同一目标。
  • Windows 替代:在 Windows 上可用 USBDeview + 任务计划或商业工具 USBDLM 触发脚本;也可用 PowerShell 结合 Task Scheduler 监听驱动器插入事件(超出本篇范围)。

八、撤销启用或卸载

若要移除自动备份:

sudo rm /etc/udev/rules.d/99-usb-backup.rules

sudo systemctl daemon-reload

sudo rm /etc/systemd/system/usb-backup@.service

sudo rm /usr/local/bin/usb-backup.sh

sudo udevadm control –reload

九、安全与备份策略提醒(务必阅读)

  1. 先备份重大数据:脚本/规则变更前请做好现有数据备份。
  2. 权限最小化:若不想以 root 写入目标目录,可创建专门用户并把 systemd 服务用 User= 指定运行(需保证可以挂载/读 U 盘或提前通过 polkit 设置)。
  3. 验证恢复流程:自动备份只是半步,定期从备份目录恢复一次文件以验证可用性。
  4. 日志监控:把 journalctl 输出或脚本日志推送到监控系统(邮件/Slack/企业微信)以便异常告警。
© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
极昼漫屿的头像 - 宋马
评论 抢沙发

请登录后发表评论

    暂无评论内容