我想在 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 启动备份脚本,您将面临两个主要问题:
- 您的脚本可能会在设备准备好并可以挂载之前启动,如果您想使用 /dev 节点挂载它,则必须保留 KERNEL=="sd*" 条件
- 更重要的是,如果您的脚本需要一些时间来执行(备份脚本很容易出现这种情况),它将在启动后不久(大约 5 秒)被终止
- 你会面临很多复杂的用户权限问题
我的建议是在您的用户主目录中创建一个脚本,该脚本侦听命名管道并将异步启动,如下所示:
#!/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 的随身碟时,它都会运行该脚本。
通过一些小的修改,可以在系统启动并运行时使用上述解决方案。