Or should I just ditch the bash script for a Python script?

Yes :-)

If you want to use bash, you could parse the file, sort it, then present it like this:

while read -r line; do
  if grep -q 'Nmap scan report for' <<<"$line"; then
     ip=$(sed 's/Nmap scan report for //' <<<"$line");
  elif grep -q 'MAC Address' <<<"$line"; then
     mac=$(sed 's/MAC Address: //' <<<"$line")
     echo "$ip $mac" >> tmp1-$$
  fi
done
sort -k 2 tmp1-$$ > tmp2-$$
lastmac=''
while read -r line; do
  ip="$(sed 's/ .*//' <<<"$line")"
  mac="$(sed -E 's/^[^ ]+ //' <<<"$line")"
  if [ "$lastmac" != "$mac" ]; then
    echo "MAC Address: $mac"
  fi
  echo "IP Address: $ip"
  lastmac=$mac
done <tmp2-$$
rm tmp1-$$ tmp2-$$

You could improve that by using bash 4 associate arrays rather than the intermediate files:

#!/bin/bash
declare -A arr
while read -r line; do
  if grep -q 'Nmap scan report for' <<<"$line"; then
     ip=$(sed 's/Nmap scan report for //' <<<"$line");
  elif grep -q 'MAC Address' <<<"$line"; then
     mac=$(sed 's/MAC Address: //' <<<"$line")
     arr["$mac"]="${arr["$mac"]} $ip"
  fi
done
for mac in "${!arr[@]}"; do
  printf "MAC Address: $mac\n"
  for ip in ${arr["$mac"]}; do
    printf "IP Address: $ip\n"
  done
done

If you prefer pipes to code, you could hack something like this:

<input (grep -E '(MAC Address|Nmap scan report for)' | sed -E -e 's/MAC Address: (.*)/\1 END/' -e 's/Nmap scan report for (.*)/\1 /' | tr -d '\n') |sed 's/ END/\n/g' | sort -k 2 > a
<a sed -E -e 's/^[^ ]+ //' | uniq | xargs -d '\n' -n 1 -I {} bash -c "echo 'MAC Address: {} '; grep '{}' a|sed -e 's/ .*//' -e 's/^/IP Address: /'"

But it’s all pretty awful. I’m sure there somewhat better perl / awk ways to do it too.

I’d run Nmap with `-oX n.xml` then use some python like:

""" parse output of sudo nmap -sP 192.168.70.0/24 -oX - >n.xml """
import xml.etree.ElementTree as ET
ROOT = ET.parse('n.xml').getroot()
IPV4S = {}
VENDORS = {}
for h in ROOT.findall('host'):
    mac = None
    ipv4 = None
    vendor = 'unknown'
    for address in h.findall('address'):
        addrtype = address.attrib['addrtype']
        if addrtype == "mac":
            mac = address.attrib['addr']
            vendor = address.attrib['vendor']
        elif addrtype == "ipv4":
            ipv4 = address.attrib['addr']
    # print "mac={},ipv4={}".format(mac,ipv4)
    if mac in IPV4S:
        IPV4S[mac].append(ipv4)
    else:
        IPV4S[mac] = [ipv4]
    if mac not in VENDORS:
        VENDORS[mac] = vendor

for mac in IPV4S:
    print "MAC Address: {} ({})".format(mac, VENDORS[mac])
    print '\n'.join(["  IP Address: {}".format(x) for x in IPV4S[mac]])

That way the parsing is more solid, and you can deal with edge cases better, and when you look at it in a few months it’s easier to see what’s going on.

— Martijn

On 29 Jul 2019, at 13:44, Mark Rogers <mark@more-solutions.co.uk> wrote:

I have a simple bash script for locating Raspberry Pis on the network:

   #!/bin/bash
   PIMAC=B8:27:EB
   sudo nmap -sP 192.168.251.0/24 | grep -i " $PIMAC" -A0 -B2

Whilst the output gives me the answer I want I'd like to tidy up the
formatting. The current result is:

   Nmap scan report for 192.168.10.20
   Host is up (-0.10s latency).
   MAC Address: B8:27:EB:22:1C:05 (Raspberry Pi Foundation)
   --
   Nmap scan report for 192.168.10.24
   Host is up (-0.10s latency).
   MAC Address: B8:27:EB:22:1C:05 (Raspberry Pi Foundation)

What I want out of it is something more like:
   MAC Address: B8:27:EB:22:1C:05 (Raspberry Pi Foundation)
   IP Address: 192.168.10.20
   IP Address: 192.168.10.24

What is the best way to do this? Or should I just ditch the bash
script for a Python script?

--
Mark Rogers // More Solutions Ltd (Peterborough Office) // 0844 251 1450
Registered in England (0456 0902) 21 Drakes Mews, Milton Keynes, MK8 0ER

_______________________________________________
main@lists.alug.org.uk
http://www.alug.org.uk/
https://lists.alug.org.uk/mailman/listinfo/main
Unsubscribe?  See message headers or the web site above!