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
  • 1
  • 8
  • 3
    Does this answer your question? [What is command substitution in a shell?](https://unix.stackexchange.com/questions/440088/what-is-command-substitution-in-a-shell) – Kamil Maciorowski Jul 01 '21 at 18:26
  • @KamilMaciorowski Maybe more something like this, not really working though ```lsusb | grep "Tascam" | awk '{printf "/usr/bin/usbreset %d", $6}'``` – Beaker Jul 02 '21 at 01:17
  • Does `usbreset` take an arbitrary number of device IDs? I.e. `usbreset ID1 ID2 ID3 ...` or only *one* at a time? – ibuprofen Jul 02 '21 at 03:07

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,781
  • 1
  • 14
  • 33
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
  • 28,789
  • 5
  • 20
  • 47