Wie führe ich ein Shellscript aus, wenn ich ein USB-Gerät einstecke?

28

Ich möchte ein Skript ausführen, wenn ich ein Gerät an meinen Linux-Computer anschließe. Führen Sie beispielsweise xinputeine Maus oder ein Backup-Skript auf einem bestimmten Laufwerk aus.

Ich habe viele Artikel darüber gesehen, zuletzt hier und hier . Aber ich kann es einfach nicht zum Laufen bringen.

Hier einige einfache Beispiele, die versuchen, zumindest eine Antwort zu erhalten.

/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

Der Regelordner wird von überwacht inotifyund sollte sofort aktiv sein. Ich stelle meine Tastatur, Maus, Tablet, Memorystick und das USB-Laufwerk immer wieder auf den neuesten Stand, aber nichts. Keine Protokolldatei berührt.

Nun, was wäre der einfachste Weg, um wenigstens zu wissen, dass etwas funktioniert? Es ist einfacher, mit etwas zu arbeiten, das funktioniert, als mit etwas, das nicht funktioniert.

Redsandro
quelle
1
Wolltest du nicht auf Unix & Linux posten ? Was ist deine Kernel-Version? Haben Sie udevadm triggerein Gerät ausgeführt oder angeschlossen, um die neue Regel anzuwenden?
Gilles 'SO- hör auf böse zu sein'
Ja, das mache ich nach jeder Änderung der Regeln, um sie auszuprobieren. Ich habe die Frage entsprechend bearbeitet. So funktioniert udev jetzt schon eine Weile, aber ich renne 3.5.0-23-generic.
Redsandro

Antworten:

24

Wenn Sie das Skript auf einem bestimmten Gerät ausführen möchten, können Sie die Hersteller- und Produkt-IDs verwenden

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

    ATTRS{idVendor}=="152d", ATTRS{idProduct}=="2329", RUN+="/tmp/test.sh"
  • in 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
    

Mit envkönnen Sie sehen, welche Umgebung in udev eingestellt ist, und mit filekönnen Sie den Dateityp ermitteln.

Die konkreten Attribute für Ihr Gerät können mit ermittelt werden lsusb

lsusb

gibt

...
Bus 001 Gerät 016: ID 152d: 2329 JMicron Technology Corp. / JMicron USA Technology Corp. JM20329 SATA-Brücke
...

Olaf Dietsche
quelle
1
Das ist interessant! Es scheint, dass es keine Berechtigung zum Schreiben in / log / hat. Es tut Schreiben in / tmp /. Ich vermute, es hatte auch keine Erlaubnis, meine vorherigen Testskripte zu lesen.
Redsandro
@Redsandro Dies war nicht beabsichtigt, nur zu Testzwecken. Wie auch immer, ich bin froh, dass es geholfen hat. ;-)
Olaf Dietsche
Ich möchte Sie ermutigen, auch diese Frage zu prüfen und zu prüfen, ob Ihr Wissen dort wertvoll sein kann. :)
Redsandro
3
Sie können auch ACTION=="add",direkt zur Regeldefinition hinzufügen .
Avindra Goolcharan
4

Hier geht es nicht direkt um Ihre Frage, sondern darum, was Sie tun. Wenn Sie ein Backup-Skript von udev aus starten, treten zwei Hauptprobleme auf:

  1. Möglicherweise wird Ihr Scrpit gestartet, bevor das Gerät bereit ist und gemountet werden kann. Sie müssen den KERNEL == "sd *" -Zustand beibehalten, wenn Sie den / dev-Knoten zum Mounten verwenden möchten
  2. Noch wichtiger ist, wenn die Ausführung Ihres Scirpt einige Zeit in Anspruch nimmt (was bei einem Backup-Skript leicht der Fall sein kann), wird es kurz nach dem Start abgebrochen (ca. 5 Sekunden).
  3. Sie werden mit vielen komplizierten Problemen mit Benutzerrechten konfrontiert sein

Mein Rat ist, ein Skript in Ihrem Benutzerhaus zu erstellen, das eine Named Pipe abhört und asynchron gestartet wird:

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

Hinweis: Ich verwende Auto-Mount mit KDE, um zu überprüfen, ob der Ordner angezeigt wird. Sie können den / dev / sd * -Parameter im FIFO aus der udev-Regel übergeben und selbst im Skript einbinden. Um in das Fifo zu schreiben, vergiss nicht, dass udev keine Shell ist und die Umleitung nicht funktioniert. Dein RUN sollte so aussehen:

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

Olivier Laporte
quelle
Gute Verwendung von Named Pipes hier. Ich habe mich gefragt, ob Sie auch einfach eine beliebige Datei in tmp erstellen und statt einer Named Pipe auch danach suchen können, oder?
Jamescampbell
1

Ich habe eine Lösung auf https://askubuntu.com/a/516336 gepostet und kopiere die Lösung auch hierher.

Ich habe mit pyudev ein Python-Skript geschrieben , das ich im Hintergrund laufen lassen möchte . Dieses Skript lauscht auf udev-Ereignisse (daher ist es sehr effizient) und führt den von mir gewünschten Code aus. In meinem Fall läuft es xinputBefehle zum Einrichten meiner Geräte ( Link auf die neueste Version ).

Hier ist eine kurze Version desselben Skripts:

#!/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()
Denilson Sá Maia
quelle
1
Sieht aus wie ein schönes Skript, +1. Eine Sache, die ich vorschlagen würde, ist die Verwendung von list anstelle von nur einer Zeichenfolge call(). Auf diese Weise foobar.shkönnen Sie das dynamisch tun, wenn es erforderlich ist, Argumente für das Skript bereitzustellen .
Sergiy Kolodyazhnyy
1
Gutes Argument. Mein "echtes" Skript (aus der Antwort verlinkt) verwendet eine Liste. In dieser minimalistischen Version, die ich hier eingefügt habe, habe ich versehentlich einen String verwendet. Vielen Dank! Ich habe die Antwort aktualisiert.
Denilson Sá Maia
-1

Um das Skript auf dem Boot auszuführen, wenn ein USB-Gerät angeschlossen ist, verwende ich die folgende Lösung:

Formatieren Sie den USB-Stick oder einen anderen USB-Speicher und geben Sie ihm dabei einen Namen. Dann in /etc/rc.local Zeile hinzufügenls -q /dev/disk/by-label > /home/pi/label.txt

Es wird eine TXT-Datei mit dem Namen label.txt erstellt (kann ein beliebiger anderer Name sein).

dann nochmal in /etc/rc.local 2 Zeilen hinzufügen:

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

Jedes Mal, wenn ein pendrive mit dem Namen USB_drive_name eingefügt wird, wird das Skript ausgeführt.

Mit ein paar kleinen Änderungen kann die obige Lösung verwendet werden, wenn das System in Betrieb ist.

Aleksander Celewicz
quelle
Beantwortet nicht die Frage: Dies gilt nur für die Boot-Zeit (und die Verwendung udevfür andere Zeiten sind keine "kleinen Änderungen") und den Raspberry Pi. Es ist eine unnötige sudo- rc.localläuft als root, es ist ein Privileg Eskalation Problem - eine Datei , die von einem normalen Benutzer editierbar ist , wird als root ausgeführt.
Gert van den Berg