Как выполнить шеллскрипт при подключении USB-устройства


28

Я хочу выполнить скрипт, когда я подключаю устройство к моей машине с 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
Разве вы не хотели писать на Unix и Linux ? Какая у вас версия ядра? Вы запускаете udevadm triggerили подключаете устройство, чтобы применить новое правило?
Жиль "ТАК - перестань быть злым"

Да, я делаю это после каждого редактирования правил, чтобы опробовать их. Я отредактировал вопрос соответственно. Это способ, которым udev работает некоторое время, но я бегу 3.5.0-23-generic.
Redsandro

Ответы:


24

Если вы хотите запустить скрипт на определенном устройстве, вы можете использовать идентификаторы поставщика и продукта

  • В /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 JMicron Technology Corp. / JMicron USA Technology Corp. JM20329 SATA Bridge
...


1
Это интересно! Кажется, что у него нет разрешения на запись в / log /. Это делает запись в / TMP /. Я полагаю, что у него тоже не было разрешения читать мои предыдущие тестовые сценарии.
Redsandro

@Redsandro Это не было преднамеренным, просто для целей тестирования. В любом случае, я рад, что это помогло. ;-)
Олаф Дитче

Я хотел бы призвать вас также проверить этот вопрос и посмотреть, могут ли ваши знания быть там полезными. :)
Redsandro

3
Вы также можете добавить ACTION=="add",непосредственно в определение правила.
Авиндра Гулчаран

4

Речь идет не о вашем вопросе, а о том, что вы делаете. Если вы запустите скрипт резервного копирования из udev, вы столкнетесь с двумя основными проблемами:

  1. Ваш scrpit может быть запущен до того, как устройство будет готово и может быть смонтирован, вы должны соблюдать условие KERNEL == "sd *", если вы хотите использовать узел / dev для его монтирования
  2. Что еще более важно, если выполнение scirpt занимает некоторое время (что может быть легко в случае со сценарием резервного копирования), оно будет убито вскоре после запуска (около 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 exists, 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 red 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 frome fifo : [$line]"
        fi
    fi
done
echo "Reader exiting"

Примечание: я использую автоматическое монтирование с kde, поэтому проверяю, появляется ли папка. Вы можете передать параметр / dev / sd * в fifo из правила udev и самостоятельно смонтировать его в сценарии. Чтобы писать в fifo, не забывайте, что udev не является оболочкой и перенаправление не работает. Ваш RUN должен быть таким:

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


Большое использование именованных каналов здесь. Мне было интересно, что вы также можете просто создать произвольный файл в tmp и искать его вместо именованного канала, правильно?
Jamescampbell

1

Я разместил решение на /ubuntu//a/516336, и я также копирую его здесь.

Я написал скрипт на Python, используя pyudev , и оставляю работать в фоновом режиме. Этот скрипт слушает события 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()

1
Похоже, хороший сценарий, +1. Одна вещь, которую я бы предложил - использовать список вместо одной строки call(). Таким образом, если необходимо предоставить аргументы foobar.shскрипту, вы можете сделать это динамически.
Сергей Колодяжный

1
Честная оценка. Мой "настоящий" скрипт (ссылка из ответа) использует список. В этой минималистической версии, которую я вставил сюда, я запутался и случайно использовал строку. Благодарность! Я обновил ответ.
Денилсон Са Майя

-1

Чтобы запустить скрипт при загрузке USB-устройства, я использую решение ниже:

Отформатируйте pendrive или любое другое USB-хранилище и присвойте ему имя при этом. Затем в /etc/rc.local строке добавленияls -q /dev/disk/by-label > /home/pi/label.txt

он создаст текстовый файл с именем label.txt (может быть любым другим именем)

затем снова в /etc/rc.local добавьте еще 2 строки:

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

Теперь каждый раз, когда pendrive с именем USB_drive_name вставляется, он запускает скрипт.

С помощью нескольких небольших модификаций вышеуказанное решение можно использовать, когда система запущена и работает.


Не отвечает на вопрос: это охватывает только время загрузки (и использование udevдля других времен не «несколько маленьких модификаций») и Raspberry Pi. Это не нужно sudo- rc.localзапускается с правами root, это проблема повышения привилегий - файл, который редактируется обычным пользователем, запускается с правами root.
Герт ван ден Берг
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.