1

I need to create a script to unbind/bind all usb in one linux machine.

For this I need to run:

lsusb -t wich returns:

root@lsdg7sd-fc:~# lsusb -t
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=ohci_hcd/10p, 12M
    |__ Port 4: Dev 2, If 0, Class=vend., Driver=ftdi_sio, 12M
    |__ Port 6: Dev 4, If 0, Class=HID, Driver=usbhid, 1.5M
    |__ Port 6: Dev 4, If 1, Class=HID, Driver=usbhid, 1.5M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci_hcd/10p, 480M

Now, for this example I need to run:

 // disable the bus 1, port 1
 echo '2-4' | tee /sys/bus/usb/drivers/usb/unbind
 // disable the bus 1, port 2
 echo '2-6' | tee /sys/bus/usb/drivers/usb/unbind
 // enable the bus 1, port 1
 echo '2-4' | tee /sys/bus/usb/drivers/usb/bind
 // enable the bus 1, port 2
 echo '2-6' | tee /sys/bus/usb/drivers/usb/bind

In order to achieve this by creating a script (I am very new to linux), I did the following:

#!/bin/bash

lsusb -t | awk '{

bus="";
if($1=="|__")
      print "Child USB Port: ",$3;
else if ($1=="/:") {
      print $3;
      var= $(echo $3 | grep -o "[1-9]");
      echo $var;
}
}'

var=`echo "02.Port" | grep -o "[1-9]"`;
echo $var;
var=`echo "02.Port" | grep -o "[0-9]\{2\}"`;
echo $var;

I tried all the possible combinations in order to set var. I always get an error.

awk: line 8: syntax error at or near grep

Can anyone suggest a solution or another approach?

Thanks for reading.

---------------------- Update

Thanks Ed Morton, the input is the result of the command lsusb -t: (at my end)

/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=ohci_hcd/10p, 12M
    |__ Port 4: Dev 2, If 0, Class=vend., Driver=ftdi_sio, 12M
    |__ Port 6: Dev 4, If 0, Class=HID, Driver=usbhid, 1.5M
    |__ Port 6: Dev 4, If 1, Class=HID, Driver=usbhid, 1.5M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci_hcd/10p, 480M

And I want to parse this information in order to get the BUS number and the port number. So the output should be:

2-1
2-4
2-6
2-6
1-1

But, I only need the "child" ports, so I need the following output:

2-4
2-6
2-6

Thanks again!

Juano7894
  • 703
  • 1
  • 8
  • 21
  • 1
    You have awk vs. shell script confusion here. Almost none of that "awk script" is actually a valid awk script. – Etan Reisner Nov 04 '14 at 16:00
  • You should also not use old and outdated back-tics, use parentheses like this: `var=$(echo "02.Port" | grep -o "[1-9]");` – Jotne Nov 04 '14 at 16:02
  • Thanks for your comments!. I will start using $() instead of the back-tics – Juano7894 Nov 04 '14 at 17:16

2 Answers2

1

shell is an environment from which to call UNIX tools, with a language to sequence those calls.

grep is a UNIX tool to print lines from files (or pipes) that match regular expressions.

awk is the standard general-purpose UNIX tool for processing text.

They are 3 completely different tools with their own languages/syntax.

You would call grep from shell to find a regexp and print the matching line.

You would call awk from shell to do more complicated text-manipulation operations which may or may not including anything that grep can do.

You would NOT call grep from awk and you CANNOT call awk from grep.

You can call shell from awk in some very rare situations where that is useful but you cannot call shell from grep.

You posted some sample input in your question, now update it to show the specific output you would like to get as a result of running a tool on that input so we can help you write the tool to do that.

In case it's useful, here is the correct awk syntax for an awk script to do what you are trying to do with echo and grep, etc.:

$ cat tst.awk
{
    if (/\|__/) {
        print "Child USB Port: ",$3
    }
    else {
        print $3
        var = $3
        gsub(/[^[:digit:]]/,"",var)
        print var
    }
}
$
$ awk -f tst.awk file
02.Port
02
Child USB Port:  4:
Child USB Port:  6:
Child USB Port:  6:
01.Port
01

Given your updated question:

$ cat tst.awk
{
    var = $3+0
    if (/\/:/) {
        bus = var
    }
    else {
        print bus "-" var
    }
}
$
$ awk -f tst.awk file
2-4
2-6
2-6

Note that if you want that to go to a couple of files as well as stdout then all you need instead of a subsequent echo+pipe+tee is:

BEGIN{
    path   = "/sys/bus/usb/drivers/usb/"
    unbind = path "unbind"
    bind   = path "bind"
}
{
    var = $3+0
    if (/\/:/) {
        bus = var
    }
    else {
        busPort = bus "-" var
        print busPort
        print busPort > unbind
        print busPort > bind
    }
}

and since it seems pointless to repeat the action for bus+port combinations you've already processed (e.g. the second 2-6):

BEGIN{
    path   = "/sys/bus/usb/drivers/usb/"
    unbind = path "unbind"
    bind   = path "bind"
}
{
    var = $3+0
    if (/\/:/) {
        bus = var
    }
    else {
        busPort = bus "-" var
        if (!seen[busPort]++) {
            print busPort
            print busPort > unbind
            print busPort > bind
        }
    }
}
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • Thanks for the reply. What I am trying to do is to execute this command. echo '2-4' | tee /sys/bus/usb/drivers/usb/unbind (then bind again.). What I need is to write a script to find the BUS and port number in order to execute this command. I am thinking in another aproach, like ls /sys/bus/usb/drivers/usb | grep "-", this will return what I need. But I want to learn, if you know another way to use the lsusb command to achieve this, will be great! – Juano7894 Nov 04 '14 at 17:11
  • 1
    I have never heard of the lsusb command, nor so I know what you mean when you talk about "then bind again", etc. I do not know the domain you are working in. What I do know is how to use shell commands to transform text from one format to another using awk and/or other UNIX tools so if you'd like help with that then please update your question to show the sample input and expected output as requested. If you simply want to `execute this command. echo '2-4' | tee /sys/bus/usb/drivers/usb/unbind` then just go ahead and execute that command but that seems totally unrelated to the input you posted – Ed Morton Nov 04 '14 at 17:17
  • 1
    I have updated the question with the information that you requested. Thanks for the help! – Juano7894 Nov 04 '14 at 17:29
  • OK, I've updated my answer to match and shown how to do what I THINK you ultimately want without a separate echo, tee and pipe afterwards. – Ed Morton Nov 04 '14 at 17:32
  • 1
    Yes, thanks you very much for your help. Not sure why the bind and unbind commands are not working. But this may be another problem, I will continue learning about this. THANKS AGAIN! – Juano7894 Nov 04 '14 at 19:00
0

You can not define variable inside awk

var= $(echo $3 | grep -o "[1-9]"

Do some like this:

awk -v var=$(echo $3 | grep -o "[1-9]" '{print var}' file

PS I do not see why you need to use both awk and grep

Jotne
  • 40,548
  • 12
  • 51
  • 55
  • I think `$3` in his script is intended to be the 3rd field on each line of his input, not the 3rd argument passed to his shell script. And correct, you would not use grep if you already are using awk. – Ed Morton Nov 04 '14 at 17:24