-
-
Save tai/31b3d5d7046e05045ec78dd3555db298 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| import sys | |
| import os | |
| import glob | |
| import subprocess as sp | |
| import re | |
| import time | |
| import logging | |
| from datetime import datetime | |
| from argparse import ArgumentParser | |
| def usage(): | |
| p = os.path.basename(sys.argv[0]) | |
| help = f""" | |
| {p} - Find USB device by string match | |
| Usage: {p} [options] <regex> [<regex> ...] | |
| Options: | |
| -b, --bus-device: Show only USB bus/device path | |
| -d, --device : Show only device path | |
| -f, --fulldate : Show time in full format | |
| -r, --recent <n>: Show recent devices within <n> seconds | |
| Example: | |
| // show USB serial device (with tty* name) | |
| // OUTPUT: time bus:dev vid:pid hub-port [dev] name | |
| # {p} tty | |
| 16:30:00 003:004 0403:6001 3-2.3 [ttyUSB0] Future Technology Devices International, Ltd FT232 Serial (UART) IC | |
| // show USB storage under specific hub | |
| # usb 1-8.1 sd | |
| 15:18:06 001:126 1908:0226 1-8.1.3 [sdi] GEMBIRD MicroSD Card Reader/Writer | |
| 15:18:06 001:125 1908:0226 1-8.1.2 [sdh] GEMBIRD MicroSD Card Reader/Writer | |
| ... | |
| Note: | |
| - Output is sorted and latter device is more recently attached. | |
| """.strip() | |
| print(help, file=sys.stderr) | |
| sys.exit(0) | |
| def usb2blk(): | |
| found = {} | |
| for i in glob.glob("/dev/sd*"): | |
| devname = os.path.basename(i) | |
| syspath = os.path.realpath(f"/sys/class/block/{devname}") | |
| cur = syspath | |
| while cur != "/sys": | |
| # skip partitions | |
| if os.path.exists(f"{cur}/partition"): | |
| break | |
| # USB device | |
| if os.path.exists(f"{cur}/busnum"): | |
| busnum = int(open(f"{cur}/busnum").readline().strip()) | |
| devnum = int(open(f"{cur}/devnum").readline().strip()) | |
| busdev = f"{busnum:03d}:{devnum:03d}" | |
| found[busdev] = devname | |
| break | |
| cur = os.path.dirname(cur) | |
| return found | |
| def usb2tty(): | |
| found = {} | |
| for i in glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*"): | |
| devname = os.path.basename(i) | |
| syspath = os.path.realpath(f"/sys/class/tty/{devname}") | |
| cur = syspath | |
| while cur != "/sys": | |
| # USB device | |
| if os.path.exists(f"{cur}/busnum"): | |
| busnum = int(open(f"{cur}/busnum").readline().strip()) | |
| devnum = int(open(f"{cur}/devnum").readline().strip()) | |
| busdev = f"{busnum:03d}:{devnum:03d}" | |
| found[busdev] = devname | |
| break | |
| cur = os.path.dirname(cur) | |
| return found | |
| # collect device info | |
| def usb2stat(): | |
| devstat = {} | |
| for i in glob.glob("/sys/bus/usb/devices/*/devnum"): | |
| devdir = os.path.dirname(i) | |
| devnum = int(open(i).readline().strip()) | |
| busnum = int(open(os.path.join(devdir, "busnum")).readline().strip()) | |
| st = os.stat(f"/dev/bus/usb/{busnum:03d}/{devnum:03d}") | |
| busdev = f"{busnum:03d}:{devnum:03d}" | |
| devstat[busdev] = { | |
| "st_ctime": st.st_ctime, | |
| "hub_port": os.path.basename(devdir) | |
| } | |
| return devstat | |
| def main(opt): | |
| pat_list = [re.compile(i, flags=re.IGNORECASE) for i in opt.args] | |
| dev2tty = usb2tty() | |
| dev2blk = usb2blk() | |
| devstat = usb2stat() | |
| # filter 'lsusb' output | |
| devlist = [] | |
| proc = sp.Popen("lsusb", stdout=sp.PIPE, text=True, bufsize=1) | |
| for line in proc.stdout: | |
| elem = line.split(None, 6) | |
| busnum = elem[1] | |
| devnum = elem[3][:-1] | |
| busdev = f"{busnum}:{devnum}" | |
| devlist.append([busdev, elem]) | |
| now = time.time() | |
| # print in order | |
| devlist.sort(key=lambda dev: devstat[dev[0]]["st_ctime"]) | |
| for dev in devlist: | |
| busdev, elem = dev | |
| vidpid = elem[5] | |
| stat = devstat[busdev] | |
| # show only recent device | |
| if opt.recent > 0 and (now - stat["st_ctime"]) > opt.recent: | |
| continue | |
| ts = datetime.fromtimestamp(stat["st_ctime"]) | |
| if opt.fulldate: | |
| date = ts.strftime("%Y-%m-%d/%H:%M:%S") | |
| else: | |
| date = ts.strftime("%H:%M:%S") | |
| tags = "" | |
| blk = dev2blk.get(busdev) | |
| if blk: tags += f"[{blk}]" | |
| tty = dev2tty.get(busdev) | |
| if tty: tags += f"[{tty}]" | |
| info = "" | |
| if tags: | |
| info = tags + " " | |
| info += elem[6] | |
| hub_port = devstat[busdev]["hub_port"] | |
| # final matching string | |
| out = f"{date} {busdev} {vidpid} {hub_port} {info}" | |
| # show only all-matching one | |
| if pat_list: | |
| check_result = [pat.search(out) for pat in pat_list] | |
| if None in check_result: | |
| continue | |
| # show list | |
| if opt.device: | |
| if blk: print(f"/dev/{blk}") | |
| if tty: print(f"/dev/{tty}") | |
| elif opt.bus_device: | |
| bus, dev = busdev.split(":", 2) | |
| print(f"/dev/bus/usb/{bus}/{dev}") | |
| else: | |
| print(out, end="") | |
| if __name__ == '__main__' and '__file__' in globals(): | |
| ap = ArgumentParser() | |
| ap.print_help = usage | |
| ap.add_argument('-D', '--debug', nargs='?', default='INFO') | |
| ap.add_argument('-b', '--bus-device', action='store_true') | |
| ap.add_argument('-d', '--device', action='store_true') | |
| ap.add_argument('-f', '--fulldate', action='store_true') | |
| ap.add_argument('-r', '--recent', type=int, default=0) | |
| ap.add_argument('args', nargs='*') | |
| opt = ap.parse_args() | |
| logging.basicConfig(level=eval('logging.' + opt.debug)) | |
| main(opt) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment