5

UPDATE I need to clarify that Jon8RFC-LT and DOMAIN are also just generic examples of dynamic content, like the IP address and MAC address; nmblookup retrieves and displays entirely dynamic content based on the ip address. If awk is used, I need to have a way to pull 4 dynamic values from the nmblookup: IP, hostname/asset name, domain name, MAC address. Sorry for the confusion, I updated the code to make it more clear.

I have been searching and using my Linux book for a couple of days and cannot find what I need for awk/gawk/grep/egrep/sed (I think I need one or more of those, elegantly). In a bash script, I'm running:

su_nmblookup=$(nmblookup -A $ipaddress)

which returns

WARNING: The "idmap backend" option is deprecated
added interface eth0 ip=a07d::a07d:a07d:a07d:a07d%eth0 bcast=b57d::ffff:ffff:ffff:ffff%eth0 netmask=ffff:ffff:ffff:ffff::
added interface eth1 ip=b57d::b57d:b57d:b57d:b57d%eth1 bcast=a07d::ffff:ffff:ffff:ffff%eth1 netmask=ffff:ffff:ffff:ffff::
added interface eth0 ip=234.234.234.234 bcast=12.12.12.12 netmask=255.255.0.0
Socket opened.
 Looking up status of 123.123.123.123
    JON8RFC-LT <00> -         B <ACTIVE> 
    DOMAIN        <00> - <GROUP> B <ACTIVE> 
    JON8RFC-LT <20> -         B <ACTIVE> 
    DOMAIN        <1e> - <GROUP> B <ACTIVE> 

    MAC Address = 4F-A2-4F-A2-4F-A2

The best I've managed, is to chop it down with this code:

display=${su_nmblookup/#*Looking/\Looking}
Looking up status of 123.123.123.123
    JON8RFC-LT <00> -         B <ACTIVE> 
    DOMAIN        <00> - <GROUP> B <ACTIVE> 
    JON8RFC-LT <20> -         B <ACTIVE> 
    DOMAIN        <1e> - <GROUP> B <ACTIVE> 

    MAC Address = 4F-A2-4F-A2-4F-A2

However, what I'd like to know is how to return either of these cleaned up formats. I want to learn how the grep/awk/sed works with extracting data with these two examples, one with preserving the formatting, and one with just new lines. I had a hell of a time even getting the quoting/coding to work properly here because of the formatting and gt/lt symbols!

Looking up status of 123.123.123.123
    JON8RFC-LT
    DOMAIN
    4F-A2-4F-A2-4F-A2

OR, simply

JON8RFC-LT
DOMAIN
123.123.123.123
4F-A2-4F-A2-4F-A2

Thank you for your help!

Jon8RFC
  • 85
  • 1
  • 8
  • You do not need sed and/or grep if you're using awk. sed is only for substituting strings for REs on a single line. grep is only for finding REs or strings in a file and printing the resulting lines. awk is for everything else. – Ed Morton Apr 28 '14 at 18:56
  • `JON8RFC-LT` in the output you want is the first `JON8RFC-LT` in the input, or second or they are both always the same? same question for `DOMAIN` – a5hk Apr 28 '14 at 19:48
  • @EdMorton I wouldn't say that `sed` is *only* for that... Granted, that is by far it's most common use, but it is actually far more capable (although not always simple to grok/use) than that... – twalberg Apr 28 '14 at 20:02
  • @twalberg `sed` is also sometimes useful for playing [code-golf](http://codegolf.stackexchange.com/questions/19450/enlarge-ascii-art/19457#19457) ;-) – Digital Trauma Apr 28 '14 at 20:07
  • @twalberg that is all sed best for (and it IS very good for it, I use it daily). For anything else you should use a different tool, usually awk. Although sed is capable of far more, the resulting code is incredibly complicated, unreadable, unmaintainable, and generally inexplicable. If you need to do anything that spans multi-line input or find yourself considering using more than the s, g, and p (with -n) sed constructs then you are using the wrong tool. – Ed Morton Apr 29 '14 at 03:11

3 Answers3

4

Assuming the output of nmblookup -A 123.123.123.123 is redirected to file input.txt

awk '/Looking up status of/ {print} /JON8RFC-LT/ {if(a!=1){print "\t"$1;a=1}} /DOMAIN/ {if(b!=1){print "\t"$1;b=1}} /MAC Address/ {print "\t"$4}' input.txt

Updated as Etan Reisner suggested to be shorter:

awk '/Looking up status of/ {print} /JON8RFC-LT/ && !a {print "\t"$1;a=1} /DOMAIN/ && !b {print "\t"$1;b=1} /MAC Address/ {print "\t"$4}' input.txt

output:

 Looking up status of 123.123.123.123
    JON8RFC-LT
    DOMAIN
    4F-A2-4F-A2-4F-A2

Updated for Dynamic content

awk '/Looking up status of/,/MAC Address/ {print; getline;print "\t"$1;getline;print "\t"$1;getline;getline;getline;getline;print "\t"$4;exit 0}' input.txt

Assumes on the two lines after line Looking up ... you want the first word. Then ignores three lines and then prints MAC address.

a5hk
  • 7,532
  • 3
  • 26
  • 40
  • 1
    You don't need this in a file for this to work. awk can operate on piped input just fine. Also an action of `{print}` is the default so you don't need to list it explicitly in the `/Looking .../` case in the first snippet. – Etan Reisner Apr 28 '14 at 17:53
  • 2
    You can also shorten things a bit by letting awk do the a/b matching for you in the pattern (e.g. `/DOMAIN/ && !b {print "\t"$1;b=1}`) – Etan Reisner Apr 28 '14 at 17:57
  • @Etan Reisner, Thanks, I have updated the answer. I usually use `print` because it is more readable. – a5hk Apr 28 '14 at 18:04
  • Thanks! I tinkered a little bit, but couldn't figure out how to extract the hostname (JON8RFC-LT) and domain name (DOMAIN) as dynamic content with the third version. – Jon8RFC Apr 28 '14 at 19:31
3

I see you want a grep/awk/sed answer, but you may be interested to know what you need is possible purely with builtins:

unset results
declare -A results
while read; do
    case $REPLY in
        *'Looking up status of '*) ip="${REPLY##* }";;
        *'MAC Address = '*) mac="${REPLY##* }";;
        *'    '*) tmp="${REPLY#    }"; results[${tmp%% *}]=1 ;;
    esac
done < <(nmblookup -A $ipaddress)
printf "%s\n" ${!results[@]}
echo $ip
echo $mac

This script fragment may be placed in your existing script.

This script reads each line of input and applies a case switch to match the patterns you are interested in. Each pattern has its own set of commands to format the data you need. In the case of lines that begin with 4 spaces, we use a bash associative array to make sure we only get one of each of the JON8RFC-LT and DOMAIN lines.

Note associative arrays require 4.0 or more.

Digital Trauma
  • 15,475
  • 3
  • 51
  • 83
  • 2
    bash 4.0 and newer only due to `declare -A` for the record. – Etan Reisner Apr 28 '14 at 18:21
  • Thanks! This looks pretty good, unfortunately I'm hoping to keep the code contained in one file, to deploy to many machines. – Jon8RFC Apr 28 '14 at 19:30
  • @Jon8RFC No need for multiple script files. I've edited the answer slightly to show how you could just add this to your existing script file, assuming that existing script starts with `#!/bin/bash` and that you are using bash 4.0 or greater. – Digital Trauma Apr 28 '14 at 20:04
  • 1
    You could make the `case` branches a little more readably with selective quoting, e.g.: `*'Looking up status of '*)` – mklement0 Apr 28 '14 at 20:28
  • @DigitalTrauma Thanks for this--it was a great learning experience, which was one of the things I was looking for. I understand how robust these things can be now, and I'll refer back to this for exporting and logging. – Jon8RFC Apr 28 '14 at 21:51
2

An annotated awk solution that provides both output formats - prettied and raw - selectable by a variable:

# Set this to:
#  * 1 for a "pretty" display with header line and indentation
#  * 0 for printing the raw data items only.
pretty=1

awk -v pretty=$pretty '
    # Skip lines before "Looking up ..."
  !startRow && /Looking up status of / { startRow=NR; }
  !startRow { next }
    # Parse the lines of interest relative to the "Looking up ..." row.
  NR==startRow { ip=$5; header=$0; next } # IP address
  NR==startRow+1 { nm=$1; next }          # name, e.g.: "JON8RFC-LT"
  NR==startRow+2 { dm=$1; next }          # domain, e.g.: "DOMAIN"
  /MAC Address =/ { ma=$4; exit }         # MAC address, e.g.: "4F-A2-4F-A2-4F-A2"
  END {         # all relevant lines processed; output result
    if (pretty) # print with header and indentation
      { print header; print "\t" nm; print "\t" dm; print "\t" ma }
    else        # print raw data items only
      { print nm; print dm; print ip; print ma }
  }' <(nmblookup -A $ipaddress)

A few quick pointers:

  • -v pretty=$pretty defines an awk variable based on a shell variable; note how the awk program as a whole is enclosed in single quotes to prevent accidental shell expansions inside the awk program, which should be treated as its own world, separate from the shell.
  • !startRow: awk variables in a numeric/Boolean context default to 0/false if not yet defined, so this expression evaluates to false until startRow is set to a non-zero value.
  • /Looking up status of/ is a regex that is matched against the current input line; NR contains the current, 1-based row (line) number.
  • next skips remaining pattern/actions for the current line and proceeds to the next line.
  • NR==startRow is a pattern that evaluates to true if the current line's index matches the value stored in startRow
  • $1, for instance, represents the 1st field from the current line - by default, awk breaks lines into fields - starting with index 1 and the ending index stored in variable NF - based on whitespace.
  • END is a special pattern whose associated block is executed after all input lines have been processed; note that an exit command in a previous action still causes the END action to be processed.
  • <(...) is an instance of process substitution which provides the output from any command as a pseudo file.
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Thanks for adding this in. I'm still very new to all of this, and I'm going to be exploring your code learn from as well. – Jon8RFC Apr 28 '14 at 21:54
  • @Jon8RFC My pleasure; `awk` is well worth learning; I've revised my answer to provide an `awk`-only solution and also added further comments. – mklement0 Apr 28 '14 at 22:50