1

This seems like a pretty popular question, but for some reason the awk solution I found here that I am trying does not work.

I have an alias that gets the info on some of my devices on my network. I want to be able to search the list and retrieve the device "name". The alias and output look like:

$ mtb devices
Alias     Address           Name         Software   Brand
------------------------------------------------------------
          192.168.1.107     Device1      1009       Some_brand
TV        192.168.1.103     Device2      0831       Some_other_brand
TV2       192.168.1.105     Device3      0831       Some_brand

In my script, using grep I can take the line I am looking for, since I search by the ip address:

IP=$1
BOX=`mtb devices | grep ${IP}`
echo $BOX

This gives me a result:

$ ./my_devices.sh 192.168.1.105
TV2 192.168.1.105 Device3 0831 Some_brand

Now I want to get the "Name" from the line, so I used awk '{print $3}':

IP=$1
BOX=`mtb devices | grep ${IP} | awk '{print $3;}'`
echo $BOX
$ ./my_devices.sh 192.168.1.105
Device3

But when I search an IP address that does not have an alias, then I obviously get the wrong result:

$ ./my_devices.sh 192.168.1.107
1009

I looked for more soltions and I found another awk solution, but the new awk is not working:

IP=$1
BOX=`mtb devices | grep ${IP} | awk '{ for(i=1;i<=NF;i++) if ($i == "$IP") print $(i+1) }'`
echo $BOX

When I run my script it returns nothing:

$ ./my_devices.sh 192.168.1.105
$

Obviously I want it to return Device3.

The IP address field and Name fields will always be populated, but the alias field may not be populated.

Ebad
  • 131
  • 11
  • `1)` instead of tildes, use this syntax: `BOX=$( command )` . `2)` your last awk will work if you pass the variable as expected: `mtb devices | awk -v ip="$IP" '{ for(i=1;i<=NF;i++) if ($i == ip) print $(i+1) }'` (also omit `grep`, only one command is enough for this, either grep or awk or sed etc) – thanasisp Sep 01 '20 at 16:40
  • 3
    Does this answer your question? [Using grep to get the next WORD after a match in each line](https://stackoverflow.com/questions/10971765/using-grep-to-get-the-next-word-after-a-match-in-each-line) – thanasisp Sep 01 '20 at 16:41
  • 2
    I've never heard of `mtb` but most tools that can produce output in a human-readable table also have options to produce the output in a more easily machine-readable format - have you checked the man page for `mtb` to see if it has such options? – Ed Morton Sep 01 '20 at 17:42

5 Answers5

3

If one observation holds for all output from mtb devices, the device name could be references as
(number of fields) - 2

IP=$1
BOX=`mtb devices | grep ${IP} | awk '{ print $(NF - 2); }'`
echo $BOX

or a shortened alternate:

mtb devices | awk "/$1/ { print \$(NF - 2); }"
Milag
  • 1,793
  • 2
  • 9
  • 8
1

1st solution considers that you want to print next field whether IP address is found. 2nd solution looks for header of Input_file and checks which field has string Name in it and then prints it, so this will be more generic as even Name is present in any field it will print it.

1st solution: Could you please try following. One need to run this script by giving ip address as an argument to script.

cat script.ksh
arg="$1"
mtb devices | 
awk -v ip="$arg" '
{
  for(j=1;j<=NF;j++){
    if(j==ip){
       print $(j+1)
    }
  }
}'


2nd solution(Generic solution): OR in case you want to get the Name field column number from very first line then following may help you.

cat script.ksh
arg="$1"
mtb devices |
awk -v ip="$arg" '
FNR==1{
  for(j=1;j<=NF;j++){
    if($j=="Name"){
       field=j
    }
  }
}
{
  for(j=1;j<=NF;j++){
    if(j==ip){
       print $field
    }
  }
}'
RavinderSingh13
  • 130,504
  • 14
  • 57
  • 93
1

Short answer:

#!/bin/bash

if [ $# -ne 1 ]; then
   read -p "enter ip: " ip
fi

awk -v ip="$ip" "/ip/{if (NF==5) {print $3}}" <<< $(mtb devices)

DR;TL

For IP (192.168.1.107) there is no name, you can check fields count very easily using special variable in awk called "NF"

mtb devices | grep '192\.168.\1\.107' | awk '{if (NF==5) {print $3}}'

This won't print anything.

mtb devices | grep '192\.168\.1\.103' | awk '{if (NF==5) {print $3}}'

This will print Device2

Note: when using regular expression keep in mind that dot character (.) represents any character, it's better to escape it like: \.

nickmarko
  • 71
  • 6
1

If you don't like awk, there's always sed:

mtb devices | sed -ne "s/.*192.168.1.107\s*\(\w\+\).*/\1/p" 
# or
mtb devices | sed -ne "s/.*$IP\s*\(\w\+\).*/\1/p" 

This will -not print any lines unless the -expression tells us otherwise. Our expression is s//p, which does s/ubstitution, and /prints the result.

If we do substitute, we grab the .*whole line.*, capture the next \word after our variable, and then replace our whole grabbed line with just the \1 group that we captured.

jeremysprofile
  • 10,028
  • 4
  • 33
  • 53
1

As a rule, grep is awesome, but when I need to do slightly more complex matching and cropping, I turn to sed to avoid spawning needless processes.

mtb devices | sed -En "/ $IP /{ s/^.* $IP +(\\S+) .*/\\1/; p; }" 

No awk needed. / $IP / replaces the grep, s/^.* $IP +(\\S+) .*/\\1/ strips everything else off the line, leaving the field following the IP. -n prevents printing unless asked, and p asks. -E keeps me from having to use a ton of backslash quoting, though since I embedded the $IP I still need an extra \ in \\1.

I actually wrote it with \s & \S and it worked, with AND without the extra \, but I'm on an emulation. Test it on your environment.

Paul Hodges
  • 13,382
  • 1
  • 17
  • 36