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新しいルールを適用するためにデバイスを実行または接続しましたか?
ジル 'SO-悪であるのをやめる'

はい、ルールを編集するたびにそれを試してみます。それに応じて質問を編集しました。これがしばらくの間udevが動作する方法ですが、私は実行してい3.5.0-23-genericます。
レッドサンドロ

回答:


24

特定のデバイスでスクリプトを実行する場合は、ベンダーIDと製品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 JMicron Technology Corp. / JMicron USA Technology Corp. JM20329 SATA Bridge
...


1
これは面白い!/ log /に書き込む権限がないようです。それはないの/ tmp /への書き込みを。以前のテストスクリプトを読む許可もなかったと思います。
レッドサンドロ

@Redsandroこれは意図的なものではなく、テスト目的のためだけのものです。とにかく、助けてくれてうれしいです。;-)
オラフディーチェ

この質問もチェックして、あなたの知識がそこで価値があるかどうかを確認することをお勧めします。:)
レッサンドロ

3
ACTION=="add",ルール定義に直接追加することもできます。
アビンドラグールチャラン16

4

これはあなたの質問についてではなく、あなたがしていることについてです。udevからバックアップスクリプトを開始すると、2つの主な問題に直面します。

  1. デバイスの準備ができてマウントできるようになる前にscrpitが開始される場合があります。/devノードを使用してマウントする場合は、KERNEL == "sd *"状態を維持する必要があります。
  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で自動マウントを使用しているため、表示されるフォルダーを確認します。udevルールからfifoで/ dev / sd *パラメータを渡し、スクリプトで自分でマウントできます。fifoに書き込むには、udevがシェルではなく、リダイレクトが機能しないことを忘れないでください。RUNは次のようになります。

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


ここで名前付きパイプの素晴らしい使用。名前付きパイプの代わりに、tmpに任意のファイルを作成して、それを探すこともできるのではないかと思っていました。
ジェームズキャンベル

1

/ubuntu//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()

1
素敵なスクリプトのように見えます、+ 1。私がお勧めすることの1つは、の1つの文字列ではなくlistを使用することですcall()。そのようにして、foobar.shスクリプトに引数を提供する必要がある場合、動的に行うことができます。
セルギーKolodyazhnyy

1
公正なポイント。(答えからリンクされた)私の「本物の」スクリプトはリストを使用します。ここに貼り付けたこのミニマリストバージョンでは、文字列を台無しにして誤って使用していました。ありがとう!答えを更新しました。
デニルソンサマイア

-1

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という名前のペンドライブが挿入されるたびに、スクリプトが実行されます。

上記のソリューションを少し変更するだけで、システムが稼働しているときにソリューションを使用できます。


質問には答えません。これは、ブート時間(およびudev他の時間に使用することは「わずかな変更」ではありません)およびRaspberry Pi のみを対象としています。不必要がありますsudo- rc.localrootとして実行すると、それは権限昇格の問題です-通常のユーザーが編集されたファイルは、rootとして実行されます。
ゲルト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.