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

Red*_*dro 40 linux bash ubuntu udev

我想在我的 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"
Run Code Online (Sandbox Code Playgroud)

/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
Run Code Online (Sandbox Code Playgroud)

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

现在,至少知道某些事情正在起作用的最简单方法是什么?使用有效的方法比使用无效的方法更容易。

Ola*_*che 35

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

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

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

lsusb
Run Code Online (Sandbox Code Playgroud)

...
Bus 001 Device 016: ID 152d:2329 JMicron Technology Corp. / JMicron USA Technology Corp. JM20329 SATA Bridge
...

  • 您还可以将 `ACTION=="add",` 直接添加到规则定义中。 (3认同)

小智 10

这不是直接关于你的问题,而是关于你在做什么。如果您从 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"
Run Code Online (Sandbox Code Playgroud)

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

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

  • @jamescampbell 该管道允许您执行“读取”操作,该操作会阻塞,直到将某些内容写入管道为止。您在“读取”期间使用 0 个 CPU 周期,这比检查需要睡眠(即轮询解决方案)的文件是否存在要好得多,并且如果您使用长时间睡眠,则检测可能会在几秒钟后发生。 (2认同)

Den*_*aia 6

我已经在https://askubuntu.com/a/516336上发布了一个解决方案,并且我还在此处复制粘贴了该解决方案。

我使用pyudev编写了一个 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()
Run Code Online (Sandbox Code Playgroud)