如何在插入 USB 设备时执行 shellscript

如何在插入 USB 设备时执行 shellscript

我想在 Linux 机器中插入设备时执行脚本。例如,xinput在鼠标上运行或在某个驱动器上运行备份脚本。

我看过很多这方面的文章,最近这里这里。但我就是无法让它发挥作用。

这里有一些简单的例子,试图获得至少某种类型的响应。

/etc/udev/rules.d/test.rules

#KERNEL=="sd*", ATTRS{vendor}=="*", ATTRS{model}=="*", ATTRS{serial}=="*", RUN+="/usr/local/bin/test.sh"
#KERNEL=="sd*", ACTION=="add", "SUBSYSTEM=="usb", ATTRS{model}=="My Book 1140    ", ATTRS{serial}=="0841752394756103457194857249", RUN+="/usr/local/bin/test.sh"
#ACTION=="add", "SUBSYSTEM=="usb", RUN+="/usr/local/bin/test.sh"
#KERNEL=="sd*", ACTION=={add}, RUN+="/usr/local/bin/test.sh"
KERNEL=="sd*", RUN+="/usr/local/bin/test.sh"
KERNEL=="*", RUN+="/usr/local/bin/test.sh"

/usr/local/bin/test.sh

#!/usr/bin/env bash
echo touched >> /var/log/test.log

if [ "${ACTION}" = "add" ] && [ -f "${DEVICE}" ]
then
    echo ${DEVICE} >> /var/log/test.log
fi

规则文件夹受到监视inotify并应立即处于活动状态。我不断地重新插入我的键盘、鼠标、平板电脑、记忆棒和 USB 驱动器,但什么也没有。没有触及日志文件。

现在,至少知道某些东西正在工作的最简单方法是什么?从有效的东西开始工作比从无效的东西开始工作更容易。

答案1

如果要在特定设备上运行脚本,可以使用供应商和产品 ID

  • /etc/udev/rules.d/test.rules

    ATTRS{idVendor}=="152d", ATTRS{idProduct}=="2329", RUN+="/tmp/test.sh"
    
  • test.sh

    #! /bin/sh
    
    env >>/tmp/test.log
    file "/sys${DEVPATH}" >>/tmp/test.log
    
    if [ "${ACTION}" = add -a -d "/sys${DEVPATH}" ]; then
    echo "add ${DEVPATH}" >>/tmp/test.log
    fi
    

使用env,您可以查看 udev 设置的环境,并且使用file,您将发现文件类型。

可以通过以下方式发现您设备的具体属性lsusb

lsusb

给出

...
总线001设备016:ID 152d:2329智微科技公司/智微美国科技公司JM20329 SATA桥
...

答案2

这与您的问题无关,而是与您正在做的事情有关。如果您从 udev 启动备份脚本,您将面临两个主要问题:

  1. 您的脚本可能会在设备准备好并可以挂载之前启动,如果您想使用 /dev 节点挂载它,则必须保留 KERNEL=="sd*" 条件
  2. 更重要的是,如果您的脚本需要一些时间来执行(备份脚本很容易出现这种情况),它将在启动后不久(大约 5 秒)被终止
  3. 你会面临很多复杂的用户权限问题

我的建议是在您的用户主目录中创建一个脚本,该脚本侦听命名管道并将异步启动,如下所示:

#!/bin/bash

PIPE="/tmp/IomegaUsbPipe"
REMOTE_PATH="/path/to/mount/point"
LOCAL_PATH="/local/path/"


doSynchronization()
{
  #your backup here
}

trap "rm -f $PIPE" EXIT

#If the pipe doesn't exist, create it
if [[ ! -p $PIPE ]]; then
    mkfifo $PIPE
fi

#If the disk is already plugged on startup, do a syn
if [[ -e "$REMOTE_PATH" ]]
then
    doSynchronization
fi

#Make the permanent loop to watch the usb connection
while true
do
    if read line <$PIPE; then
        #Test the message read from the fifo
        if [[ "$line" == "connected" ]]
        then
            #The usb has been plugged, wait for disk to be mounted by KDE
            while [[ ! -e "$REMOTE_PATH" ]]
            do
                sleep 1
            done
            doSynchronization
        else
            echo "Unhandled message from fifo : [$line]"
        fi
    fi
done
echo "Reader exiting"

注意:我使用 kde 自动挂载,因此我检查文件夹是否出现。您可以从 udev 规则中传递 fifo 中的 /dev/sd* 参数,并在脚本中自行挂载。要写入 fifo,请不要忘记 udev 不是 shell,并且重定向不起作用。你的跑步应该是这样的:

RUN+="/bin/sh -c '/bin/echo 连接 >> /tmp/IomegaUsbPipe'"

答案3

我已经发布了一个解决方案https://askubuntu.com/a/516336我还将解决方案复制粘贴到这里。

我用Python编写了一个脚本普尤德夫我让其在后台运行。该脚本监听 udev 事件(因此,它非常高效)并运行我想要的任何代码。就我而言,它运行xinput命令来设置我的设备链接到最新版本)。

这是同一脚本的简短版本:

#!/usr/bin/env python3

import pyudev
import subprocess

def main():
    context = pyudev.Context()
    monitor = pyudev.Monitor.from_netlink(context)
    monitor.filter_by(subsystem='usb')
    monitor.start()

    for device in iter(monitor.poll, None):
        # I can add more logic here, to run different scripts for different devices.
        subprocess.call(['/home/foo/foobar.sh', '--foo', '--bar'])

if __name__ == '__main__':
    main()

答案4

要在插入 USB 设备时在启动时运行脚本,我使用以下解决方案:

格式化随身碟或任何其他 USB 存储设备并在执行此操作时为其命名。然后在/etc/rc.local 添加行ls -q /dev/disk/by-label > /home/pi/label.txt

它将创建一个名为 label.txt 的 txt 文件(可以是任何其他名称)

然后再次在 /etc/rc.local 中添加另外 2 行:

if  grep -q USB_drive_name /home/pi/label.txt; then
sudo /home/pi/script.sh

现在,每次插入名为 USB_drive_name 的随身碟时,它都会运行该脚本。

通过一些小的修改,可以在系统启动并运行时使用上述解决方案。

相关内容