Udev问题:分离脚本等待USB设备自动挂载未达预期
我之前也碰到过类似的Udev挂载时机问题,咱们先理清楚你的现有配置和核心痛点:
你的现有Udev规则
KERNEL=="sdc", ACTION=="add", SUBSYSTEMS=="usb", ATTRS{manufacturer}=="Bookeen", ATTRS{product}=="Cybook", RUN+="/home/nap/Scripts/Wallabag.sh"
触发的脚本内容
#!/bin/bash sleep 5 ( exec </dev/null >/home/nap/usb.log 2>&1 set -x mkdir /media/nap/Cybook/Test ) &
问题根源
Udev是在设备刚被系统识别的早期阶段触发规则的,此时负责自动挂载的服务(比如udisks2)还在处理设备初始化、文件系统检测等操作——这个过程的耗时完全不确定,哪怕你加了sleep 5,也没法保证挂载一定完成(比如设备读取慢、系统负载高的时候,5秒可能根本不够)。另外,Udev会管理它启动的子进程生命周期,后台运行的脚本进程可能会被Udev的环境限制,没法正常等待挂载完成。
可靠解决方案:用Systemd服务替代直接RUN脚本
现代Linux系统推荐用Systemd来处理这类依赖系统状态(比如挂载)的任务,它能精准等待挂载完成后再执行你的脚本,完全不需要靠sleep猜时间:
步骤1:修改Udev规则,触发Systemd服务
把原来的Udev规则改成下面这样,让它触发一个自定义的Systemd服务,而非直接运行脚本:
KERNEL=="sdc", ACTION=="add", SUBSYSTEMS=="usb", ATTRS{manufacturer}=="Bookeen", ATTRS{product}=="Cybook", TAG+="systemd", ENV{SYSTEMD_WANTS}="cybook-mount.service"
步骤2:创建Systemd服务文件
在/etc/systemd/system/目录下创建cybook-mount.service文件,内容如下:
[Unit] Description=处理Cybook USB设备挂载后执行脚本 RequiresMountsFor=/media/nap/Cybook After=media-nap-Cybook.mount [Service] Type=oneshot ExecStart=/home/nap/Scripts/Wallabag.sh StandardOutput=journal+console StandardError=journal+console [Install] WantedBy=multi-user.target
这里的RequiresMountsFor和After指令会强制Systemd等待/media/nap/Cybook挂载完成后才启动服务,从根源上避免了挂载延迟的问题。
步骤3:简化你的脚本
去掉原来的sleep和后台执行逻辑,因为Systemd已经帮你处理好时机了:
#!/bin/bash set -x # 把日志输出到指定文件,同时也可以通过journalctl查看 exec >>/home/nap/usb.log 2>&1 mkdir /media/nap/Cybook/Test
步骤4:重新加载配置
完成上面的修改后,执行下面的命令让配置生效:
sudo udevadm control --reload-rules sudo systemctl daemon-reload
备选方案:手动挂载设备(如果挂载点不固定)
如果你的Cybook设备挂载点不固定(比如可能因为设备标签变化、多设备冲突导致挂载点改变),可以在脚本里用udisksctl手动挂载,灵活性更高:
#!/bin/bash set -x exec >>/home/nap/usb.log 2>&1 # 定义挂载点 MOUNT_POINT="/media/nap/Cybook" # 确保挂载点目录存在 mkdir -p "$MOUNT_POINT" # 手动挂载设备(注意替换为你的实际设备分区,比如sdc1) udisksctl mount --block-device /dev/sdc1 --mount-point "$MOUNT_POINT" # 执行你的操作 mkdir "$MOUNT_POINT/Test"
不过这种方式还是不如Systemd依赖挂载点的方式可靠,因为手动挂载也需要等待设备就绪,而Systemd的依赖管理更完善。
内容的提问来源于stack exchange,提问作者Nap

