I using NMAP, I ran a scan on a large network to see open ports. The output file is 2MB, but I want to filter out all of the IP addresses with ALL closed ports.
Nmap scan report for 10.x.x.x
Host is up (0.048s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
23/tcp open telnet
Nmap scan report for 10.x.x.x
Host is up (0.046s latency).
All 1000 scanned ports on 10.x.x.x are closed
Nmap scan report for 10.x.x.x
Host is up (0.045s latency).
All 1000 scanned ports on 10.x.x.x are closed
Should output to only output to:
Nmap scan report for 10.x.x.x
Host is up (0.048s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
23/tcp open telnet
EDIT
The results are like
Nmap scan report for 10.x.x.x
Host is up (0.048s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
23/tcp open telnet
Nmap scan report for 10.x.x.x
Host is up (0.046s latency).
All 1000 scanned ports on 10.x.x.x are closed
Nmap scan report for 10.x.x.x
Host is up (0.045s latency).
All 1000 scanned ports on 10.x.x.x are closed
There are newlines that didn't copy over correctly
EDIT Thanks everyone. I see awk is pretty awesome and easy to do.
As I see it, you're trying to apply some intelligent filtering to your nmap output, not just a simple "grep".
Since your nmap command (per your comments rather than your question) is pointing at a subnet rather than an individual host, you need to interpret each section of output individually. But this kind of interpretation is too complex for a regular expression. (Might be possible with PREG, but it would be extremely difficult to compose and virtually impossible to read.) A tool like awk
is a much better choice for this task.
For example:
nmap 10.10.0.0/16 | awk '
/^Nmap scan report for/ {
if (open) {
print output;
}
output="";
open=0;
}
{
output=output $0 "\n";
}
$2 == "open" {
open=1;
}
END {
if (open) {
print output;
}
}
'
Awk is easy enough to read, but you should know that it operates by matching each line of input against expressions that look like condition { action }
. If a condition evaluates to true, the action is executed. So the first one has a condition that is a regular expression to find the beginning of a host section, and the actions are encased in curly braces. The second condition is missing, and so is assumed to be "true" for all lines. The last condition matches after all lines of input have been processed, and is necessary in case the last host scanned includes open ports.
This kind of thing could be expressed much more densely, but I've written it long so that you can see how the logic works more easily. Tighter code will come with practice.
Note that you can put the awk script into a separate file, which you reference using awk's -f
option. Read the man page for details.
You could also put this whole thing into its own shell script if you don't want to keep the awk portion in its own file. You should be able to find examples of what that looks like easily enough -- at any rate, it's out of scope for this question.