Cách thực hiện shellscript khi tôi cắm thiết bị USB


28

Tôi muốn thực thi một tập lệnh khi tôi cắm một thiết bị vào máy Linux của mình. Ví dụ: chạy xinputtrên chuột hoặc bản sao lưu trên một ổ đĩa nhất định.

Tôi đã thấy rất nhiều bài viết về điều này, gần đây nhất ở đâyở đây . Nhưng tôi không thể làm cho nó hoạt động được.

Dưới đây là một số ví dụ đơn giản đang cố gắng nhận được ít nhất một số loại phản hồi.

/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

Thư mục quy tắc được theo dõi inotifyvà sẽ được kích hoạt ngay lập tức. Tôi tiếp tục thay thế bàn phím, chuột, máy tính bảng, thẻ nhớ và ổ đĩa USB, nhưng không có gì. Không có tệp nhật ký chạm vào.

Bây giờ, cách đơn giản nhất để ít nhất biết một cái gì đó đang hoạt động là gì? Làm việc từ một thứ gì đó không hiệu quả thì dễ hơn làm.


1
Không phải bạn muốn đăng lên Unix & Linux sao? Phiên bản kernel của bạn là gì? Bạn đã chạy udevadm triggerhoặc cắm một thiết bị để áp dụng quy tắc mới?
Gilles 'SO- ngừng trở nên xấu xa'

Vâng, tôi làm điều đó sau mỗi lần chỉnh sửa các quy tắc để thử chúng. Tôi chỉnh sửa câu hỏi cho phù hợp. Đây là cách udev hoạt động được một thời gian, nhưng tôi đang chạy 3.5.0-23-generic.
Redsandro

Câu trả lời:


24

Nếu bạn muốn chạy tập lệnh trên một thiết bị cụ thể, bạn có thể sử dụng id nhà cung cấp và sản phẩm

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

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

Với env, bạn có thể xem môi trường nào được đặt từ udev và với file, bạn sẽ khám phá loại tệp.

Các thuộc tính cụ thể cho thiết bị của bạn có thể được phát hiện với lsusb

lsusb

cho

...
Bus 001 Thiết bị 016: ID 152d: 2329 JMicron Technology Corp / JMicron USA Technology Corp JM20329 Cầu nối
...


1
Hay đấy! Có vẻ như nó không được phép ghi vào / log /. Nó không ghi vào / tmp /. Tôi đoán nó cũng không được phép đọc các bài kiểm tra trước đây của tôi.
Redsandro

@Redsandro Đây không phải là chủ ý, chỉ nhằm mục đích thử nghiệm. Dù sao, tôi rất vui vì nó đã giúp. ;-)
Olaf Dietsche

Tôi muốn khuyến khích bạn cũng kiểm tra câu hỏi này và xem liệu kiến ​​thức của bạn có thể có giá trị ở đó không. :)
Redsandro

3
Bạn cũng có thể thêm ACTION=="add",trực tiếp vào định nghĩa quy tắc.
Avindra Goolcharan

4

Đây không phải là trực tiếp về câu hỏi của bạn mà là về những gì bạn đang làm. Nếu bạn bắt đầu một tập lệnh sao lưu từ udev, bạn sẽ phải đối mặt với hai vấn đề chính:

  1. Bạn có thể bắt đầu kiểm tra trước khi thiết bị sẵn sàng và có thể được gắn kết, bạn phải giữ điều kiện KERNEL == "sd *" nếu bạn muốn sử dụng nút / dev để gắn kết
  2. Quan trọng hơn, nếu scirpt của bạn mất một chút thời gian để thực thi (có thể dễ dàng xảy ra với một tập lệnh sao lưu), nó sẽ bị giết ngay sau khi nó được khởi động (khoảng 5s)
  3. Bạn sẽ phải đối mặt với nhiều vấn đề về quyền người dùng phức tạp

Lời khuyên của tôi là tạo một tập lệnh trong nhà người dùng của bạn, nghe một ống có tên và nó sẽ được bắt đầu không đồng bộ như:

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

Lưu ý: Tôi sử dụng tự động gắn kết với kde vì vậy tôi kiểm tra thư mục xuất hiện. Bạn có thể truyền tham số / dev / sd * trong fifo từ quy tắc udev và tự gắn nó vào tập lệnh. Để viết trong fifo đừng quên rằng udev không phải là một cái vỏ và chuyển hướng đó không hoạt động. CHẠY của bạn sẽ giống như:

CHẠY + = "/ bin / sh -c '/ bin / echo được kết nối >> / tmp / IomegaUsbPipe'"


Sử dụng tuyệt vời của các ống được đặt tên ở đây. Tôi đã tự hỏi bạn cũng có thể chỉ cần tạo một tệp tùy ý trong tmp và tìm kiếm nó thay vì một ống có tên, đúng không?
jamescampbell

1

Tôi đã đăng một giải pháp trên https://askubfox.com/a/516336 và tôi cũng đang sao chép giải pháp ở đây.

Tôi đã viết một kịch bản Python bằng pyudev mà tôi không chạy trong nền. Kịch bản đó nghe các sự kiện udev (do đó, nó rất hiệu quả) và chạy bất kỳ mã nào tôi muốn. Trong trường hợp của tôi, nó chạy xinputcác lệnh để thiết lập các thiết bị của tôi ( liên kết đến phiên bản mới nhất ).

Đây là một phiên bản ngắn của cùng một kịch bản:

#!/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
Trông giống như một kịch bản đẹp, +1. Một điều tôi khuyên bạn nên sử dụng danh sách thay vì chỉ một chuỗi call(). Theo cách đó, nếu cần thiết phải cung cấp các đối số cho foobar.shtập lệnh, bạn có thể thực hiện điều đó một cách linh hoạt.
Sergiy Kolodyazhnyy

1
Điểm công bằng. Kịch bản "thực" của tôi (được liên kết từ câu trả lời) sử dụng một danh sách. Trong phiên bản tối giản này tôi đã dán ở đây, tôi đã nhầm lẫn và vô tình sử dụng một chuỗi. Cảm ơn! Tôi đã cập nhật câu trả lời.
Denilson Sá Maia

-1

Để chạy tập lệnh khi khởi động khi cắm thiết bị usb, tôi sử dụng giải pháp bên dưới:

Định dạng Pendrive hoặc bất kỳ bộ lưu trữ usb nào khác và đặt tên cho nó khi làm như vậy. Sau đó, /etc/rc.local thêm dòngls -q /dev/disk/by-label > /home/pi/label.txt

nó sẽ tạo một tệp txt có tên là nhãn.txt (có thể là bất kỳ tên nào khác)

sau đó một lần nữa trong /etc/rc.local thêm 2 dòng khác:

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

Bây giờ, mọi lúc mọi nơi đều có tên USB_drive_name, nó sẽ chạy tập lệnh.

Với một vài sửa đổi nhỏ, giải pháp trên có thể được sử dụng khi hệ thống hoạt động.


Không trả lời câu hỏi: Điều này chỉ bao gồm thời gian khởi động (và sử dụng udevcho những lần khác không phải là "một vài sửa đổi nhỏ") và Raspberry Pi. Có một điều không cần thiết sudo- rc.localchạy với quyền root, đó là vấn đề leo thang đặc quyền - một tệp có thể chỉnh sửa bởi người dùng bình thường được chạy dưới quyền root.
Gert van den Berg
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.