How to execute a shellscript when I plug-in a USB-device

If you want to run the script on a specific device, you can use the vendor and product ids

  • 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
    

With env, you can see what environment is set from udev and with file, you will discover the file type.

The concrete attributes for your device can be discovered with lsusb

lsusb

gives

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


This isn't directly about your question but about what you're doing. If you start a backup script from udev you will face two main issues :

  1. Your script might be started before the device is ready and can be mounted, you have to keep the KERNEL=="sd*" condition if you want to use the /dev node to mount it
  2. More important, if your script takes some time to execute (which can easily be the case with a backup script) it will be killed shortly after it is started (about 5s)
  3. You will face many complicated user permission issues

My advice is to create a script in your user home which listens to a named pipe and which will be started asynchronously like :

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

Note : I use auto-mount with kde so I check for the folder to appear. You can pass the /dev/sd* parameter in the fifo from the udev rule and mount it yourself in the script. To write to the fifo don't forget that udev is not a shell and that redirection doesn't work. Your RUN should be like :

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


I've posted a solution on https://askubuntu.com/a/516336 and I'm also copy-pasting the solution over here.

I wrote a Python script using pyudev that I leave running in the background. That script listens to udev events (thus, it's very efficient) and runs whatever code I want. In my case, it runs xinput commands to setup my devices (link to the most recent version).

Here's a short version of the same script:

#!/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()