udev 中 bash 脚本的不同行为

udev 中 bash 脚本的不同行为

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

#!/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

如果我直接在终端中调用脚本,一切都会正常工作。因此日志文件中的返回内容就是这样的。

start
sdc1_vfat

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

start

循环不再起作用。

/dev/udev/ 中的规则如下

SUBSYSTEM=="usb", ACTION=="add", RUN+="/bin/bash /usr/bin/myScript.sh"

谁知道这是什么原因造成的?


这里有一些更多信息,并根据您的回答建议了一个重新修改的版本。

这是 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"

安装脚本现在如下所示:

#!/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

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

我仍然不知道现在的问题:

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

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


附加信息

刚刚发现,报告了syslogan中的内容exit code 127。所以这似乎是权限问题。但我如何确保udev脚本以 root 身份运行?

答案1

Bash 的行为是一样的...它只是lsblk返回一个空字符串...原因是,在处理完插入同一磁盘所触发的所有规则之前,它udev不会使您的磁盘可用。/dev

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

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

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

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

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

  • 用 运行它sudo /bin/bash scriptfile
  • 添加一个 cron-job(启动过程完成后只需运行一次) 将其添加到 root 的 crontab 中sudo crontab -e
  • 添加systemd服务(并启用服务)来运行它。
#!/bin/bash

# Set the name of the partition to be monitored.
mpartition="/dev/sdb1"
# Set the full path to the mount-point.
mount_point="/home/user/USB_DRIVE"
# Set the full path to the logfile(will be created if it doesn't exist)
log_file="/home/user/usb_mount.log"

# Start "inotifywait"(need be installed first with "sudo apt install inotify-tools") to monitor the partition 
inotifywait -q -m --include "$mpartition" -e create -e delete /dev/ | 

while read -r directory event partition; do
    if [ "$event" == "CREATE" ]; then
        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
        status="$?"
        if [ "$status" -eq 0 ]; then
            echo "$(date): Successful mount with exit code $status [$note]" >> "$log_file"
        else
            echo "$(date): Failed mount with exit code $status [$note]" >> "$log_file"
        fi
    elif [ "$event" == "DELETE" ]; then
        note=$(/usr/bin/umount -v "$mount_point" 2>&1)
        status="$?"
        if [ "$status" -eq 0 ]; then
            echo "$(date): Successful un-mount with exit code $status [$note]" >> "$log_file"
        else
            echo "$(date): Failed un-mount with exit code $status [$note]" >> "$log_file"
        fi
    fi
done

监视特定磁盘上的特定分区

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

$ ls /dev/disk/
by-id  by-label  by-partlabel  by-partuuid  by-path  by-uuid

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

usb-SanDisk_Cruzer_Blade_4C530200811130110350-0:0
usb-SanDisk_Cruzer_Blade_4C530200811130110350-0:0-part1

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

$ [ "$(readlink -e /dev/disk/by-id/usb-SanDisk_Cruzer_Blade_4C530200811130110350-0:0)" ] && echo "connected"
connected

/dev并像这样知道它的名字readlink

$ readlink -f /dev/disk/by-id/usb-SanDisk_Cruzer_Blade_4C530200811130110350-0:0
/dev/sdb  

所以,脚本就变成这样:

#!/bin/bash

# Set the ID of the partition to be monitored.
mpartition="usb-SanDisk_Cruzer_Blade_4C530200811130110350-0:0-part1"
# Set the full path to the mount-point.
mount_point="/home/user/USB_DRIVE"
# Set the full path to the logfile(will be created if it doesn't exist)
log_file="/home/user/usb_mount.log"

# Start "inotifywait"(need be installed first with "sudo apt install inotify-tools") to monitor the partition 
inotifywait -q -m --include "$mpartition" -e create -e delete /dev/disk/by-id/ | 

while read -r directory event partition; do
    if [ "$event" == "CREATE" ]; then
        device=$(/usr/bin/readlink -f /dev/disk/by-id/"$mpartition")
        note=$(/usr/bin/mount -v -o rw "$device" "$mount_point" 2>&1) # If needed, add extra options after -o like "-o rw,umask=000" to allow all write and read
        status="$?"
        if [ "$status" -eq 0 ]; then
            echo "$(date): Successful mount with exit code $status [$note]" >> "$log_file"
        else
            echo "$(date): Failed mount with exit code $status [$note]" >> "$log_file"
        fi
    elif [ "$event" == "DELETE" ]; then
        note=$(/usr/bin/umount -v "$mount_point" 2>&1)
        status="$?"
        if [ "$status" -eq 0 ]; then
            echo "$(date): Successful un-mount with exit code $status [$note]" >> "$log_file"
        else
            echo "$(date): Failed un-mount with exit code $status [$note]" >> "$log_file"
        fi
    fi
done

相关内容