udev 中 bash 脚本的不同行为

haf*_*sch 7 command-line bash scripts udev

我创建了一个小示例脚本,它是我的真实脚本的简化版本,它看起来基本上如下:

#!/bin/bash

echo "start" > /home/myName/log.txt

#get list with all attached devices
for i in $(lsblk -lo name,fstype,hotplug,type|grep '1 part$'|tr -s ' ' ' '|sed 's/ 1 part$//'|grep ' ..*$'|tr ' ' '_')
do

  echo $i >> /home/myName/log.txt
     
done
Run Code Online (Sandbox Code Playgroud)

如果我直接在终端中调用脚本,则一切正常。所以日志文件中的返回是类似的。

start
sdc1_vfat
Run Code Online (Sandbox Code Playgroud)

但是一旦从 udev 规则调用脚本,循环就不再起作用。日志文件中的输出仅

start
Run Code Online (Sandbox Code Playgroud)

循环不再起作用。

/dev/udev/ 中的规则如下所示

SUBSYSTEM=="usb", ACTION=="add", RUN+="/bin/bash /usr/bin/myScript.sh"
Run Code Online (Sandbox Code Playgroud)

有人知道原因可能是什么吗?


这里有一些更多信息以及建议的基于您的答案的修改版本。

这里是 udev 规则:

# check usb sdb1 plugged unplugged
SUBSYSTEM=="block", ACTION=="add", RUN+="/bin/bash /usr/bin/usb_mount.sh"
SUBSYSTEM=="block", ACTION=="remove", RUN+="/bin/bash /usr/bin/usb_unmount.sh"
Run Code Online (Sandbox Code Playgroud)

安装脚本现在看起来如下:

#!/bin/bash

#general definitions
MNT_PATH="/media/USB_DRIVE"
DEV_ID="sdc1"
    
# check connected device
udevadm info -q all "/dev/$DEV_ID" | tr '\n' ' ' | grep "/dev/$DEV_ID" | grep "ID_FS_VERSION=FAT32"

# check return
if [ $? -eq 0 ]
then
  # create folder and mount
  mkdir -p $MNT_PATH
  mount -o rw,user,exec,umask=0000 "/dev/$DEV_ID" $MNT_PATH
  exit
fi
Run Code Online (Sandbox Code Playgroud)

如果我检查安装命令,那么exit code is 0它已成功处理。已正确创建文件夹,但未安装驱动器。如果直接在终端中调用脚本,一切都会按预期工作。但如果它被调用,udev它就不会被安装。

我现在还是不明白这个问题:

  • 即使udevadm info -q all /dev/sdb*返回已连接的设备,这是否是一个计时问题?
  • 是权限问题还是路径问题?

我有什么想法可以找到原因吗?


附加信息

刚刚弄清楚,在报告syslogexit code 127。所以这似乎是一个权限问题。但如何确保udev脚本以 root 身份运行呢?

Raf*_*ffa 4

Bash 行为是相同的 \xe2\x80\xa6 它\xe2\x80\x99s 只是lsblk返回一个空字符串 \xe2\x80\xa6 原因是\xe2\x80\x99 不会让你的磁盘在处理完所有内容之前udev可用/dev通过插入同一磁盘触发的规则。

\n

因此,您监视和安装该磁盘分区的方法根本行不通。

\n

或者,您可以通过以下两种方式之一实现您的目标。

\n

监视任何磁盘上的特定分区名称

\n

这是一个更便携(独立于udisks规则udev)的 bash 脚本,运行时将监视您指定的某个 USB(或其他)磁盘分区(例如假设/dev/sdb1始终/dev/sdb空闲并为插入的第一个 USB 磁盘保留),安装它插入 USB 磁盘时指定的挂载点,拔出 USB 磁盘时卸载它,并将所有事件记录到您指定的日志文件中...阅读脚本中的注释以获取帮助。

\n

该脚本需要以管理员权限运行...因此:

\n
    \n
  • 运行它sudo /bin/bash scriptfile
  • \n
  • 添加一个 cron-job(它只需要在引导过程完成后运行一次),将其添加到 root 的 crontab 中sudo crontab -e
  • \n
  • 添加一个systemd服务(并启用该服务)来运行它。
  • \n
\n
#!/bin/bash\n\n# Set the name of the partition to be monitored.\nmpartition="/dev/sdb1"\n# Set the full path to the mount-point.\nmount_point="/home/user/USB_DRIVE"\n# Set the full path to the logfile(will be created if it doesn\'t exist)\nlog_file="/home/user/usb_mount.log"\n\n# Start "inotifywait"(need be installed first with "sudo apt install inotify-tools") to monitor the partition \ninotifywait -q -m --include "$mpartition" -e create -e delete /dev/ | \n\nwhile read -r directory event partition; do\n    if [ "$event" == "CREATE" ]; then\n        note=$(/usr/bin/mount -v -o rw "$directory$partition" "$mount_point" 2>&1) # If needed, add extra options after -o like "-o rw,umask=000" to allow all write and read\n        status="$?"\n        if [ "$status" -eq 0 ]; then\n            echo "$(date): Successful mount with exit code $status [$note]" >> "$log_file"\n        else\n            echo "$(date): Failed mount with exit code $status [$note]" >> "$log_file"\n        fi\n    elif [ "$event" == "DELETE" ]; then\n        note=$(/usr/bin/umount -v "$mount_point" 2>&1)\n        status="$?"\n        if [ "$status" -eq 0 ]; then\n            echo "$(date): Successful un-mount with exit code $status [$note]" >> "$log_file"\n        else\n            echo "$(date): Failed un-mount with exit code $status [$note]" >> "$log_file"\n        fi\n    fi\ndone\n
Run Code Online (Sandbox Code Playgroud)\n

监控某个磁盘上的某个分区

\n

有很多方法可以识别某个磁盘上的某个分区...最合适的一种是连接您要使用的 USB 磁盘,然后查看下面的内容/dev/disk/以找到在系统上识别磁盘的其他方法...这样做,ls /dev/disk/您就会发现得到:

\n
$ ls /dev/disk/\nby-id  by-label  by-partlabel  by-partuuid  by-path  by-uuid\n
Run Code Online (Sandbox Code Playgroud)\n

上面的每一个都是一个包含磁盘符号链接的目录,名称说明了一切,剩下的就是让您选择如何识别您的磁盘......一种更可靠的方法是,因为by-id这应该是是一个唯一的固定字符串,包含磁盘设备本身的制造商名称和序列号,后面是磁盘上分区的分区号...一个有用的事情是 USB 磁盘也以 .. 为前缀usb-。 .所以ls /dev/disk/by-id/会产生这样的结果:

\n
usb-SanDisk_Cruzer_Blade_4C530200811130110350-0:0\nusb-SanDisk_Cruzer_Blade_4C530200811130110350-0:0-part1\n
Run Code Online (Sandbox Code Playgroud)\n

一旦您识别出设备的 ID,您就可以简单地检查它是否已连接,例如:

\n
$ [ "$(readlink -e /dev/disk/by-id/usb-SanDisk_Cruzer_Blade_4C530200811130110350-0:0)" ] && echo "connected"\nconnected\n
Run Code Online (Sandbox Code Playgroud)\n

并知道它的名字,如下/dev所示readlink

\n
$ readlink -f /dev/disk/by-id/usb-SanDisk_Cruzer_Blade_4C530200811130110350-0:0\n/dev/sdb  \n
Run Code Online (Sandbox Code Playgroud)\n

所以,脚本就变成了这样:

\n
$ ls /dev/disk/\nby-id  by-label  by-partlabel  by-partuuid  by-path  by-uuid\n
Run Code Online (Sandbox Code Playgroud)\n