How do I uniquely identify an USB-device?

前端 未结 6 1472
心在旅途
心在旅途 2021-01-13 02:32

I was wondering how to get the unique id of a USB storage device. I already know how to fetch the SCSI serial id from this post : USB-drive serial number under linux C++ Th

6条回答
  •  醉话见心
    2021-01-13 03:19

    Generalizing Simon Rigét's answer, I came up with this bash function that, given optional vendor id and product id, returns a list of device node names, related to that vendor id and that product id if given.

    getDevNodes() {
        if [ -n "$1" ] && [ "$1" != "no_class" ]; then
            2>/dev/null find -L /sys/class/$1 -maxdepth 2 -mindepth 2 -name uevent -exec realpath "{}" +
        else
            find /sys/devices -name uevent
        fi | {        
            if [ -z "$1" ]; then
                readarray -t lines < <(find /sys/class -maxdepth 2 -mindepth 2 -type l -print -exec realpath "{}" +)
    
                local -i count=${#lines[@]} sys_dev=count/2 sys_class=0
                local -A classes
    
                while [ $sys_dev -lt $count ]; do
                        class="${lines[$sys_class]#/*/*/}"
                        class="${class%/*}"
                        classes["${lines[$sys_dev]}"]="$class"
    
                        sys_dev+=1
                        sys_class+=1                    
                done
            fi
    
            readarray -t uevents
    
            for u in "${uevents[@]}"; do       
                DEVNAME=; DEVTYPE=no_type; while IFS="=" read key value; do {
                    [ "$key" = "DEVNAME" ] && DEVNAME=/dev/"$value" 
                } || {
                    [ "$key" = "DEVTYPE" ] && DEVTYPE="$value"                 
                }; done < "$u"
    
                if [ -n "$DEVNAME" ]; then              
                    path="${u%/uevent}"
                    while [ "$path" != "/sys/devices" ] && ! [ -f "$path"/idVendor ]; do
                        path="${path%/*}"
                    done
    
                    [ "$path" != "/sys/devices" ] && {
                        read readIdVendor < "$path"/idVendor
                        read readIdProduct < "$path"/idProduct
                    } || {
                        readIdVendor=----
                        readIdProduct=----
                    }
    
                    echo "${1:-${classes[${u%/uevent}]:-no_class}}" "$DEVTYPE" "$readIdVendor" "$readIdProduct" "$DEVNAME" 
                fi
            done
        } | grep "^${1:-[[:graph:]]\+} ${2:-[[:graph:]]\+} ${3:-....} ${4:-....}" | cat
    }
    

    For instance, this is what my lsusb tells me:

    $ lsusb
    Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
    Bus 001 Device 008: ID 0bda:b719 Realtek Semiconductor Corp. 
    Bus 001 Device 006: ID 0bda:57b5 Realtek Semiconductor Corp. 
    Bus 001 Device 004: ID 0bda:0129 Realtek Semiconductor Corp. RTS5129 Card Reader Controller
    Bus 001 Device 097: ID 1004:6344 LG Electronics, Inc. G2 Android Phone [tethering mode]
    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    

    So, if I wanted to see which device nodes are associated with the vendor id 0x1004 and the product id 0x6344 I'd do the following:

    $ getDevNodes "" "" 1004 6344
    no_class        usb_device      1004 6344 /dev/bus/usb/001/097 
    tty             no_type         1004 6344 /dev/ttyACM0 
    

    So we've got two device nodes, one of which is of class tty with no devtype and the other is of unknown class but with a devtype usb_device.

    One can also only give the vendor id, like this:

    $ getDevNodes "" "" 0bda 
    no_class        usb_device      0bda 0129 /dev/bus/usb/001/004 
    no_class        usb_device      0bda b719 /dev/bus/usb/001/008 
    no_class        no_type         0bda 57b5 /dev/media0 
    video4linux     no_type         0bda 57b5 /dev/video0 
    input           no_type         0bda 57b5 /dev/input/event14 
    no_class        usb_device      0bda 57b5 /dev/bus/usb/001/006 
    

    If I only wanted video4linux class devices whose vendor id is 0bda, then I'd do the following:

    $ getDevNodes video4linux "" "" 0bda
    video4linux     no_type         0bda 57b5 /dev/video0 
    

    Arguments are basically filters over the complete list of device nodes and their associated info. Omitting one of those arguments, or using the empty string "" as an argument, disables the filter for that specific argument.

    Arguments are given in this order: 1: the class, 2: the type, 3: the vendor id, 4: the product id.


    Here follows a lite version of the above function that runs faster at the expense of some functionalities: the device nodes are printed without the additional info and there's no filter for the device type.

    getDevNodesLite() {
        if [ -n "$1" ]; then
            2>/dev/null find -L /sys/class/$1 -maxdepth 2 -mindepth 2 -name uevent -exec realpath "{}" +
        else
            find /sys/devices -name uevent
        fi | {
            if [ -n "$2" ]; then
                readarray -t uevents              
    
                for u in "${uevents[@]}"; do
                    path="${u%/uevent}"
                    while [ "$path" != "/sys/devices" ] && ! [ -f "$path"/idVendor ]; do
                        path="${path%/*}"
                    done
    
                    [ "$path" != "/sys/devices" ] && read readValue < "$path"/idVendor && [ "$readValue" = "$2" ] && {
                        if [ -n "$idProduct" ]; then
                            read readValue < "$path"/idProduct && [ "$readValue" = "$3" ]
                        fi
                    } && echo "$u"
                done
            else
                cat
            fi
        } | {
            readarray -t uevents              
    
            [ ${#uevents[@]} -gt 0 ] && sed -n 's,DEVNAME=\(.*\),/dev/\1,p' "${uevents[@]}"
        }
    }
    

提交回复
热议问题