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
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[@]}"
}
}