1

I have a USB device that I wish to reset automatically, there are two USB devices currently, but at times there may be more.

[user1@gs10 devt]$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 007: ID 1604:10c0 Tascam Dell Integrated Hub

Things to note:

  • The device number will change periodically (firmware issues (my end))
  • The ID will be dynamic as different Tascam Hubs are plugged in
  • The string may also change, whitespaces, dashes.

The device I wish to reset is Device 007. My method to identify the device ID is as follows:

[user1@gs10 devt]$ lsusb | grep "Tascam" | awk '{printf $6 "\n"}'
1d6b:0003

Works OK. Now I want to run the kernerl's usbreset command as a one liner without manually typing in the vendor ID, any reccomendations?

My attempt and goal:

lsusb | grep "Tascam" | awk '{printf "/usr/bin/usbreset %d", $6}'
[user1@gs10 devt]$ sudo /usr/bin/usbreset 1d6b:0003
Beaker
  • 67

2 Answers2

4

One option could be to use system() directly from within awk:


1. Validate

Validate by requesting device from lsusb:

lsusb | awk '/Tascam.*Hub$/{ system("lsusb -d " $6) }' 

That would take vendor:product, (field 6), from whole lines matching:

  • Tascam<Anything zero or more times>Hub<END-OF-LINE>

and execute: lsusb -d vendor:product

This would execute on all matching Tascam hubs.


2. Real call

Actually call usbreset:

lsusb | sudo awk '/Tascam.*Hub$/{ system("/usr/bin/usbreset " $6) }' 

Or perhaps better, exit if error:

lsusb | sudo awk '/Tascam.*Hub/{ if (system("/usr/bin/usbreset " $6)) exit 1 }' 

As system(expression) return exit status of the command, and 0 is success, one can use if () to check as if status is <> 0 it would proceed with exit 1. Exit with something else then 0 to signal error.

Optionally use != 0 if you find that easier to read. And perhaps throw in an error.

lsusb | sudo awk '
/Tascam.*Hub/ { 
    if (system("/usr/bin/usbreset " $6) != 0) {
        print "usbreset failed" >"/dev/stderr"
        exit 1 
    } 
}'

3. Wrapper + filter on Device

If you want to filter by device-number, it would likely be best to wrap it in a shell-script (which you likely would anyhow), and then do something like:

#! /bin/sh -

Check argument is given

if [ -z "$1" ]; then printf 'Missing device number\n' >&2 exit 1 fi

Check sudo or abort

sudo echo >/dev/null || exit 1

lsusb | sudo awk -v dev="$1" ' BEGIN { dev = sprintf("%03d:", dev) eno = 1 } $4 == dev && /Tascam.*Hub/ { if (system("/usr/bin/usbreset " $6) != 0) { eno = 2 } else { eno = 0 } # Exit on first match of device-number + name exit } END { if (eno == 0) print "OK" else if (eno == 1) print "No device found" >"/dev/stderr" else if (eno == 2) print "usbreset failed" >"/dev/stderr" exit eno }'

Note on usbreset

Not able to install it locally, but looks like some take argument as:

/dev/bus/usb/<bus>/<device-number>

In that case you might need to use something like:

# Testing:

system(sprintf("ls -l /dev/bus/usb/%03d/%03d", $2, $4))

Testing:

system(sprintf("/usr/bin/usbreset /dev/bus/usb/%03d/%03d", $2, $4))


4. Note on things in question:

  1. grep when awk is next up is an unnecessary step. awk matches regular expressions – and it in this case it is a simple string (in your code).

awk '/^foo/{ this line starts with foo }' etc.

  1. printf $6 "\n"

Looks like you want to print $6 + line-feed. That would be either of:

print $6
printf "%s\n", $6

It also tells awk to use $6 as format string. This can go bad, and is in general not a good thing to do. If $6 was for example foo%dbar, awk would expect a digit as arameter. etc.

  1. printf "/usr/bin/usbreset %d", $6, say "print digit" and here value is "$6". As field six is hex:hex the printf statement will only print the first digits, if any, else zero.
  • 12a1:06df -> 12
  • a112:3619 -> 0
ibuprofen
  • 2,890
2

It sounds like this might be what you're trying to do:

$ cat file | awk '$7=="Tascam"{print $6}' | xargs echo sudo /usr/bin/usbreset
sudo /usr/bin/usbreset 1604:10c0

or:

$ echo sudo /usr/bin/usbreset "$(cat file | awk '$7=="Tascam"{print $6}')"
sudo /usr/bin/usbreset 1604:10c0

Replace cat file with lsusb and remove the echo when you're happy with the output and want to actually execute the command currently being echoed.

Ed Morton
  • 31,617