Linux系统用户的专属福利:除了lsusb,如何利用usb.ids文件离线查询所有USB设备VID/PID信息?
Linux系统深度实践:离线高效查询USB设备VID/PID的完整指南
当你身处没有网络连接的机房,或是调试嵌入式设备时,突然需要确认一个USB设备的厂商信息,该怎么办?对于Linux系统用户来说,答案就藏在系统深处的一个小文件中——usb.ids。这个不起眼的文本文件,实际上是Linux系统管理USB设备的秘密武器。
1. 理解USB设备标识体系
每个USB设备都带有两个关键标识符:VID(Vendor ID)和PID(Product ID)。这两个16位数字组合,构成了USB设备的"身份证":
- VID:由USB-IF(USB Implementers Forum)分配给设备制造商
- PID:由制造商自行定义,标识具体产品型号
在Linux系统中,这些标识符的映射关系被存储在一个结构化的文本数据库中。不同于需要联网查询的在线服务,本地usb.ids文件提供了随时可用的离线查询能力,特别适合以下场景:
- 无网络环境的服务器机房
- 嵌入式设备开发调试
- 自动化脚本中的设备识别
- 批量处理多个USB设备信息
2. 定位系统中的usb.ids文件
不同Linux发行版中,usb.ids文件可能存放在以下几个位置:
| 文件路径 | 常见发行版 | 备注 |
|---|---|---|
/usr/share/misc/usb.ids | Debian/Ubuntu, Fedora | 最普遍的位置 |
/var/lib/usbutils/usb.ids | 较新的系统 | 由usbutils包维护 |
/usr/share/hwdata/usb.ids | 某些Red Hat系系统 | 替代位置 |
要确认你的系统中是否存在该文件,可以执行:
find /usr /var -name "usb.ids" 2>/dev/null如果系统中没有找到该文件,可能需要安装usbutils软件包:
# Debian/Ubuntu sudo apt install usbutils # RHEL/CentOS sudo yum install usbutils3. 解析usb.ids文件结构
usb.ids文件采用层级化的文本格式,主要包含三部分内容:
- 厂商列表:以VID开头,后接厂商名称
- 产品列表:在厂商条目下,以PID开头,后接产品名称
- 设备类信息:定义USB设备类、子类和协议代码
典型条目示例:
1234 Example Manufacturer 5678 Sample Product A 9abc Sample Product B理解这个结构后,我们可以利用各种文本处理工具从中提取所需信息。
4. 实战查询技巧
4.1 基础查询方法
使用grep命令进行简单查询:
# 查询特定VID对应的厂商 grep -i "^1234" /usr/share/misc/usb.ids # 查询特定VID和PID组合 grep -A1 -i "^1234" /usr/share/misc/usb.ids | grep -i "5678"4.2 高级查询脚本
对于更复杂的查询需求,可以创建可重用的脚本:
#!/bin/bash USBIDS_FILE="/usr/share/misc/usb.ids" if [ $# -lt 1 ]; then echo "Usage: $0 <VID> [PID]" exit 1 fi VID=$(echo "$1" | tr '[:lower:]' '[:upper:]') PID=$2 if [ -z "$PID" ]; then # 只查询VID grep -m1 -i "^${VID}" "$USBIDS_FILE" else # 查询VID和PID组合 PID=$(echo "$PID" | tr '[:lower:]' '[:upper:]') awk -v vid="$VID" -v pid="$PID" ' BEGIN {found_vid=0} $1 == vid {found_vid=1; print; next} found_vid && /^\t/ && $1 == pid {print; exit} found_vid && /^\t/ && $1 != pid {next} found_vid && !/^\t/ {exit} ' "$USBIDS_FILE" fi将此脚本保存为usbquery并赋予执行权限后,可以方便地查询:
./usbquery 093A ./usbquery 093A 25104.3 批量处理多个设备
当需要处理多个USB设备信息时,可以结合lsusb和脚本处理:
lsusb | awk '{print $6}' | while IFS=: read vid pid; do echo "Device $vid:$pid:" ./usbquery "$vid" "$pid" done5. 维护与更新本地数据库
虽然离线查询很方便,但usb.ids文件需要定期更新以包含最新的设备信息。
5.1 手动更新方法
# 下载最新usb.ids文件 sudo wget http://www.linux-usb.org/usb.ids -O /usr/share/misc/usb.ids # 或者使用curl sudo curl -o /usr/share/misc/usb.ids http://www.linux-usb.org/usb.ids5.2 自动化更新
创建定期更新的cron任务:
# 每月1号凌晨3点更新 0 3 1 * * root /usr/bin/curl -s -o /usr/share/misc/usb.ids http://www.linux-usb.org/usb.ids5.3 验证文件完整性
更新后建议检查文件有效性:
head -n 10 /usr/share/misc/usb.ids # 应该看到类似内容: # # List of USB ID's # # # # Maintained by Stephen J. Gowdy <linux.usb.ids@gmail.com> # # If you have any new entries, please submit them via # # http://www.linux-usb.org/usb-ids.html6. 系统集成与应用实例
6.1 在Python中解析usb.ids
def get_usb_info(vid, pid=None): with open('/usr/share/misc/usb.ids', 'r', encoding='iso-8859-1') as f: current_vid = None for line in f: line = line.strip() if not line or line.startswith('#'): continue if not line.startswith('\t'): # VID行 parts = line.split(maxsplit=1) if len(parts) == 2 and parts[0].lower() == vid.lower(): current_vid = parts[1] if pid is None: return current_vid elif current_vid and pid: # PID行 parts = line.strip().split(maxsplit=1) if len(parts) == 2 and parts[0].lower() == pid.lower(): return f"{current_vid} - {parts[1]}" return None6.2 与udev规则结合
创建自定义udev规则,自动识别特定设备:
# /etc/udev/rules.d/99-usb-custom.rules ACTION=="add", SUBSYSTEM=="usb", ENV{ID_VENDOR_ID}=="1234", ENV{ID_MODEL_ID}=="5678", SYMLINK+="my_device"6.3 嵌入式系统中的应用考虑
在资源受限的嵌入式系统中:
- 可以只保留需要的VID/PID条目,减小文件大小
- 将文件放入只读文件系统节省空间
- 使用压缩版本(如usb.ids.gz)节省存储
# 创建精简版usb.ids grep -e "^1234" -e "^abcd" /usr/share/misc/usb.ids > minimal.usb.ids7. 性能优化与高级技巧
对于频繁查询的应用场景,可以考虑以下优化方案:
7.1 数据库转换
将文本文件转换为SQLite数据库提高查询效率:
#!/bin/bash INPUT="/usr/share/misc/usb.ids" OUTPUT="/var/lib/usb_ids.db" # 创建SQLite数据库 sqlite3 "$OUTPUT" <<EOF CREATE TABLE vendors (vid TEXT PRIMARY KEY, name TEXT); CREATE TABLE products (vid TEXT, pid TEXT, name TEXT, PRIMARY KEY (vid, pid)); BEGIN; EOF # 解析并导入数据 awk -v DB="$OUTPUT" ' /^#/ {next} /^[^\t]/ && NF>=2 { vid = $1 name = substr($0, index($0,$2)) print "INSERT INTO vendors VALUES (\"" vid "\", \"" name "\");" >> DB } /^\t/ && NF>=2 { pid = $1 name = substr($0, index($0,$2)) print "INSERT INTO products VALUES (\"" vid "\", \"" pid "\", \"" name "\");" >> DB } ' "$INPUT" # 提交事务并创建索引 sqlite3 "$OUTPUT" <<EOF COMMIT; CREATE INDEX idx_products_vid ON products (vid); CREATE INDEX idx_products_pid ON products (pid); EOF7.2 内存缓存方案
对于需要极低延迟的应用,可以将数据加载到内存中:
import sqlite3 from collections import defaultdict class USBCache: def __init__(self, db_path='/var/lib/usb_ids.db'): self.conn = sqlite3.connect(db_path) self.vendor_cache = {} self.product_cache = defaultdict(dict) self._load_data() def _load_data(self): # 加载厂商数据 for vid, name in self.conn.execute("SELECT vid, name FROM vendors"): self.vendor_cache[vid.lower()] = name # 加载产品数据 for vid, pid, name in self.conn.execute("SELECT vid, pid, name FROM products"): self.product_cache[vid.lower()][pid.lower()] = name def get_vendor(self, vid): return self.vendor_cache.get(vid.lower()) def get_product(self, vid, pid): return self.product_cache.get(vid.lower(), {}).get(pid.lower())7.3 与系统监控工具集成
结合lsusb和usb.ids创建增强型监控脚本:
#!/bin/bash # 增强版lsusb,显示完整厂商和设备信息 lsusb | while read -r line; do bus=$(echo "$line" | awk '{print $2}') dev=$(echo "$line" | awk '{print $4}' | tr -d ':') vid_pid=$(echo "$line" | awk '{print $6}') vid=${vid_pid%:*} pid=${vid_pid#*:} vendor=$(./usbquery "$vid") product=$(./usbquery "$vid" "$pid") printf "Bus %s Device %s: ID %s %s\n" "$bus" "$dev" "$vid_pid" "$product" done