0

Given the output of nmap against a subnet

Nmap scan report for 192.168.1.20
Host is up (0.010s latency).
MAC Address: EC:13:B2:E2:20:48 (Netonix)
Nmap scan report for 192.168.1.21
Host is up (0.010s latency).
MAC Address: EC:13:B2:E2:13:68 (Netonix)
Nmap scan report for 192.168.1.23
Host is up (0.010s latency).
MAC Address: EC:13:B2:E1:AE:A8 (Netonix)
Nmap scan report for 192.168.1.99
Host is up (0.00076s latency).
MAC Address: 90:6C:AC:48:86:DA (Fortinet)

The goal is to get an output for csv or any other delimited format:

IP,MAC,RTT
192.168.1.20,EC:13:B2:E2:20:48,0.010s
192.168.1.21,EC:13:B2:E2:13:68,0.010s
...

The above has been accomplished in a two step process using AWK

$time_date=$(date "+%Y-%m-%d_%T")    # Get the immediate date and time
$nmap_command=$(nmap -sP 192.168.1.0/24 -n --max-rtt-timeout 50ms)

# Concatenate all lines including and between the patterns "Nmap scan report for"
c_list=$((awk '/Starting|done:/ {next} /Nmap scan report for/{ if (x) print x; x=""; }{ x=(!x)?$0:x" "$0; }END{ print x; }')<<<$nmap_command)

# Extract the values of interest from each line, add a header line, then add delimiter for every value extracted
list=$((awk -v OFS=',' -v date=$time_date 'BEGIN{ print "Date,IP,MAC,RTT" };{ gsub(/[()]/,""); for (I=1;I<=NF;I++) if ($I == "for") ip=$(I+1); else if ($I == "up") lat=$(I+1); else if ($I == "Address:") mac=$(I+1); } { print date, ip, mac, lat }')<<<$c_list)

printf '%s\n' "$list" > ~/Desktop/list.csv  # Send output to file

exit 0

The question is can the above two step awk process be consolidated into a single awk command. Thank you for the all the input beforehand

EDIT attempts to consolidate the two step with a some variations of

x_list=$((awk '/Starting|done:/ {next} /Nmap scan report for/{ if (x) print x; x=""; }{ x=(!x)?$0:x" "$0; }END I=x BEGIN{ print "Date,IP,MAC,RTT" };{ gsub(/[()]/,""); for (I=1;I<=NF;I++) if ($I == "for") ip=$(I+1); else if ($I == "up") lat=$(I+1); else if ($I == "Address:") mac=$(I+1); } { print date, ip, mac, lat }')<<<$list_ips)

was not promising. AWK would always give a good reason why to cease processing at the gap between the END and BEGIN. The exercise is actually an attempt to create a (bash) function that would be able to render down any data that is in a repeating pattern, the nmap output is just an example.

2
  • You might want to consider using nmap's -oX (XML) output, then parsing that with something like xmlstarlet instead. See for example Parsing Nmap's XML output with XMLStarlet Commented Dec 22, 2019 at 3:59
  • Thank you steeldriver for the input, I did not edit the question quick enough to convey the reason for using nmap as an example. I did know that nmap provides an output that would be easier to massage. The goal of the question was to get a consolidated method of rendering 'any' repeating pattern output into simpler data.
    – VRS
    Commented Dec 22, 2019 at 4:02

1 Answer 1

0

The normal thing to do is to store the needed info from all the lines into awk variables and then print out the data on the final line in the block. So for the example you would have something like this (I have not coded the host down latency case as the example data doesn't show it).

nmap -sP 192.168.1.0/24 -n --max-rtt-timeout 50ms |
  awk '/Nmap scan report for / {ip=$5}
       /Host is up/ { latency=$4 ; sub(/(/,"",latency) }
       /MAC Address: / { mac=$3; print(ip "," mac "," latency) ; ip=mac=latency="unknown" }
       BEGIN { print "IP,MAC,RTT"}

clearing the variables in the final line of the block may or may not be required, depending on if the data is always complete or not. If there are a lot of variables then consider using an array rather than scalar variables like ip, mac, latency as these can all be cleared with a single delete.

2
  • Thank you icarus for the solution. There is always a better way of doing it when others get to see the problem. list=$((awk '/Nmap scan report for / {ip=$5} \ /Host is up/ { latency=$4 ; sub(/[()]/,"",latency) } \ /MAC Address: / { mac=$3; print(ip "," mac "," latency) ; ip=mac=latency="unknown" } \ BEGIN { print "IP,MAC,RTT"}')<<<$list_ips) I verified the output consistency by changing the nmap command to include host down Nmap scan report for 192.168.1.100 [host down]
    – VRS
    Commented Dec 22, 2019 at 17:06
  • Unless there is a good reason, I would just pipe the nmap output into the awk program and send the output of the awk program into the list.csv file, rather than reading them into shell variables and redirecting them. There are a few reasons, it uses less memory - not too much of a concern these days, it makes it more difficult to put into the background if you are typing the command at the console rather than putting it in a script, and it increases the time to run as you need all the nmap output before you even start the awk processing.
    – icarus
    Commented Dec 22, 2019 at 18:53

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.