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
3
  • 3
    Does this answer your question? What is command substitution in a shell? Jul 1, 2021 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 2, 2021 at 1:17
  • Does usbreset take an arbitrary number of device IDs? I.e. usbreset ID1 ID2 ID3 ... or only one at a time?
    – ibuprofen
    Jul 2, 2021 at 3:07

2 Answers 2

3

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
1

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.