U盘自动入库

阿里云教程3周前发布
10 0 0

一、前言

需要把外部 U 盘 / USB 存储自动“入库”归档、备份并记录元信息?本篇给出极简、可落地的方案:在一台 Linux(可用 Raspberry Pi)上通过 udev 触发 + 挂载脚本 + rsync 备份 + SQLite 记录,实现 U 盘一插即存、自动去重与日志留存。脚本短小、步骤直接可复制粘贴。

U盘自动入库

二、适用场景

  • 会议现场、实验室、公司要聚焦收集多人的 U 盘资料;
  • 希望自动化拷贝 U 盘内容并记录来源、时间与大小;
  • 需要一个本地化、无需人工干预的“U 盘入库”流程。

三、准备(目标主机示例:Ubuntu / Debian / Raspberry Pi OS)

sudo apt update

sudo apt install -y rsync sqlite3 udev

# 可选安装 ntfs-3g 支持 NTFS 文件系统写入

sudo apt install -y ntfs-3g

创建存储与工作目录:

sudo mkdir -p /srv/usb_archive /opt/usb_ingest

sudo chown $(whoami):$(whoami) /srv/usb_archive /opt/usb_ingest

U盘自动入库

四、设计思路(简要)

  1. udev 规则检测到 USB 存储设备插入,调用处理脚本并传入设备节点(如 /dev/sda1)。
  2. 脚本读取设备的唯一标识(LABEL / UUID / SERIAL),在 /srv/usb_archive/<serial>/<timestamp>/ 下挂载并用 rsync -a 复制文件。
  3. 复制完成后调用 Python 日志脚本写入 SQLite(记录设备 id、挂载点、文件大小、文件数、时间、来源 IP/备注等)。
  4. 卸载设备并清理临时目录。

五、实现步骤(全部可复制)

1)SQLite 日志脚本(用于记录每次入库)

保存为 /opt/usb_ingest/log_db.py:

#!/usr/bin/env python3

# /opt/usb_ingest/log_db.py

import sqlite3,sys,time,os

DB=”/opt/usb_ingest/usb_archive.db”

def init():

os.makedirs(os.path.dirname(DB), exist_ok=True)

conn=sqlite3.connect(DB)

c=conn.cursor()

c.execute('''CREATE TABLE IF NOT EXISTS archives (

id INTEGER PRIMARY KEY AUTOINCREMENT,

device_id TEXT,

label TEXT,

mount_point TEXT,

path TEXT,

files INTEGER,

bytes INTEGER,

ts INTEGER,

note TEXT

)''')

conn.commit()

conn.close()

def log(device_id,label,mount_point,path,files,bytes,note=””):

conn=sqlite3.connect(DB)

c=conn.cursor()

c.execute('INSERT INTO archives (device_id,label,mount_point,path,files,bytes,ts,note) VALUES (?,?,?,?,?,?,?,?)',

(device_id,label,mount_point,path,files,bytes,int(time.time()),note))

conn.commit()

conn.close()

if __name__ == “__main__”:

init()

if len(sys.argv)>=7:

log(*sys.argv[1:7], note=(sys.argv[7] if len(sys.argv)>7 else “”))

else:

print(“usage: log_db.py device_id label mount_point path files bytes [note]”)

赋可执行权限:

chmod +x /opt/usb_ingest/log_db.py

2)主入库脚本(udev 调用它)

保存为
/opt/usb_ingest/ingest_usb.sh:

#!/bin/bash

# /opt/usb_ingest/ingest_usb.sh

set -e

DEVNODE=”$1″ # e.g. /dev/sda1

WORKDIR=”/opt/usb_ingest/work”

ARCHIVE_BASE=”/srv/usb_archive”

LOGSCRIPT=”/opt/usb_ingest/log_db.py”

mkdir -p “$WORKDIR”

# sleep 小延迟以确保设备就绪

sleep 1

# 获取识别信息(优先用 ID_SERIAL_SHORT,否则用 UUID 或 device name)

DEVNAME=$(basename “$DEVNODE”)

UDEV_INFO=$(udevadm info –query=property –name=”$DEVNODE” 2>/dev/null || true)

ID_SERIAL=$(echo “$UDEV_INFO” | awk -F= '/ID_SERIAL_SHORT=/{print $2; exit}')

ID_FS_LABEL=$(echo “$UDEV_INFO” | awk -F= '/ID_FS_LABEL=/{print $2; exit}')

ID_FS_UUID=$(echo “$UDEV_INFO” | awk -F= '/ID_FS_UUID=/{print $2; exit}')

DEVICE_ID=”${ID_SERIAL:-${ID_FS_UUID:-${DEVNAME}}}”

LABEL=”${ID_FS_LABEL:-unknown}”

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

TARGET_DIR=”$ARCHIVE_BASE/$DEVICE_ID/$TIMESTAMP”

mkdir -p “$TARGET_DIR”

# 挂载点临时目录

MOUNTPOINT=”$WORKDIR/$DEVNAME”

mkdir -p “$MOUNTPOINT”

# 尝试自动挂载(支持 vfat, exfat, ntfs, ext*)

mount “$DEVNODE” “$MOUNTPOINT” || {

# 若默认 mount 失败,尝试 ntfs-3g

mount -t auto “$DEVNODE” “$MOUNTPOINT” || {

echo “mount failed for $DEVNODE” >&2

exit 1

}

}

# 使用 rsync 复制,保留权限与时间;排除某些系统文件夹(可自定义)

rsync -a –exclude='System Volume Information' –exclude='$RECYCLE.BIN' “$MOUNTPOINT/” “$TARGET_DIR/”

# 统计文件数与大小(字节)

FILE_COUNT=$(find “$TARGET_DIR” -type f | wc -l)

BYTE_COUNT=$(du -sb “$TARGET_DIR” | cut -f1)

# 记录到 sqlite(调用 Python 脚本)

python3 “$LOGSCRIPT” “$DEVICE_ID” “$LABEL” “$MOUNTPOINT” “$TARGET_DIR” “$FILE_COUNT” “$BYTE_COUNT” “auto-ingest”

# 卸载并清理

sync

umount “$MOUNTPOINT” || { echo “umount failed for $MOUNTPOINT” >&2; }

rmdir “$MOUNTPOINT” || true

# 可选:给管理员发通知(例如写到 /var/log)

logger “USB ingest: $DEVICE_ID $LABEL -> $TARGET_DIR files=$FILE_COUNT bytes=$BYTE_COUNT”

exit 0

赋可执行权限并确认可运行:

chmod +x /opt/usb_ingest/ingest_usb.sh

3)udev 规则(检测块设备插入并触发脚本)


/etc/udev/rules.d/99-usb-ingest.rules 写入:

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

# 当 USB 可移动块设备(非分区)插入时触发(使用 KERNEL==”sd?1″ 可以改为更宽松)

ACTION==”add”, KERNEL==”sd[b-z][0-9]”, SUBSYSTEM==”block”, ENV{ID_BUS}==”usb”, RUN+=”/opt/usb_ingest/udev_wrapper.sh %k”

说明:规则匹配 USB 块设备的分区(如 sdb1, sdc1)。调整 KERNEL 匹配以符合你系统。

由于 udev 直接调用脚本时环境有限,提议用一个轻 wrapper 去后台执行脚本。创建
/opt/usb_ingest/udev_wrapper.sh:

#!/bin/bash

# /opt/usb_ingest/udev_wrapper.sh

DEVNAME=”$1″

DEVNODE=”/dev/$DEVNAME”

# 把实际工作放到后台避免 udev 超时

/usr/bin/nohup /opt/usb_ingest/ingest_usb.sh “$DEVNODE” >/opt/usb_ingest/ingest_$DEVNAME.log 2>&1 &

exit 0

赋权限并重载 udev:

chmod +x /opt/usb_ingest/udev_wrapper.sh

sudo udevadm control –reload

sudo udevadm trigger

4)测试流程

  1. 插入 U 盘(含数据)。
  2. 检查日志文件 /opt/usb_ingest/ingest_sdb1.log 或 system log (sudo journalctl -f)。
  3. 成功时,备份目录示例:/srv/usb_archive/<device_id>/<YYYY-MM-DD_HHMMSS>/,并在 SQLite 中有一条记录:

sqlite3 /opt/usb_ingest/usb_archive.db “SELECT id,device_id,label,path,files,bytes,datetime(ts,'unixepoch','localtime') FROM archives ORDER BY ts DESC LIMIT 5;”

六、安全与注意事项

  • 唯一标识:有些 U 盘没有 ID_SERIAL_SHORT,脚本会退回到 UUID 或设备名。若需要更强的唯一性,可结合 lsblk -o NAME,SERIAL 或读取 filesystem signature。
  • 权限与安全:udev 脚本以 root 环境触发。ingest_usb.sh 可能会以 root 权限拷贝文件 —— 若你希望以普通用户身份保存,请在脚本中 su -c 或用 runuser 切换用户。
  • 恶意 U 盘:自动挂载有风险(自动执行、损坏系统等)。若环境不可信,提议改为人工确认或在隔离环境中运行。
  • 文件冲突/去重:当前方案按时间目录保存所有文件,若想去重可在拷贝后对文件计算 SHA256 并只保存新文件(可在 rsync 前后增加 dedupe 步骤)。
  • 日志体积:长期大量 U 盘入库会占用大量磁盘,提议配合定期清理策略与备份策略。

七、故障排查(常见)

  • udev 未触发:sudo udevadm monitor –environment –udev 插入设备看事件是否产生;检查规则文件名是否正确且重载了 udev。
  • 脚本报错:查看 /opt/usb_ingest/ingest_*.log 与 sudo journalctl -u systemd-udevd -g udev。
  • 挂载失败:手动 sudo mount /dev/sdb1 /mnt 检查文件系统类型或缺少 ntfs-3g 等驱动。

八、扩展想法(非必须)

  • 在入库同时计算并存储每个文件的 SHA256 到数据库以便去重与索引。
  • 把 SQLite 的记录同步到中央服务器(rsync / scp)或生成 nightly 报告邮件。
  • 对拷贝的文件按类型分类(图片/文档/视频)并生成缩略图供快速预览(需额外依赖)。

九、总结

本文提供一个轻量、可复制的 U 盘自动入库方案:通过 udev 自动触发、挂载并用 rsync 入库,同时用 SQLite 记录元信息。脚本短小明了,几条命令即可在 Raspberry Pi 或任意 Linux 主机上实现“插即存”的入库流程。

© 版权声明

相关文章

暂无评论

none
暂无评论...