views:

463

answers:

7

I just learned about ngrep, a cool program that lets you easily sniff packets that match a particular string.

The only problem is that it can be hard to see the match in the big blob of output. I'd like to write a wrapper script to highlight these matches -- it could use ANSI escape sequences:

echo -e 'This is \e[31mRED\e[0m.'

I'm most familiar with Perl, but I'm perfectly happy with a solution in Python or any other language. The simplest approach would be something like:

while (<STDIN>) {
   s/$keyword/\e[31m$keyword\e[0m/g;
   print;
}

However, this isn't a nice solution, because ngrep prints out hash marks without newlines whenever it receives a non-matching packet, and the code above will suppress the printing of these hashmarks until the script sees a newline.

Is there any way to do the highlighting without inhibiting the instant appearance of the hashmarks?

+1  A: 

It shouldn't be too hard if you have the answer this question.

(Essentially, read one character at a time and if it's a hash, print it. If it isn't a hash, save the character to print out later.)

Jon Ericson
Well, yes -- I'm the one who asked that question! I'm trying to figure out what's the elegant way to do this -- should I be using nonblocking I/O? getc()? Do I really need to write my own readline() replacement? Sample code would be really helpful here.
raldi
+2  A: 

Ah, forget it. This is too much of a pain. It was a lot easier to get the source to ngrep and make it print the hash marks to stderr:

--- ngrep.c     2006-11-28 05:38:43.000000000 -0800
+++ ngrep.c.new 2008-10-17 16:28:29.000000000 -0700
@@ -687,8 +687,7 @@
     }

     if (quiet < 1) {
-        printf("#");
-        fflush(stdout);
+      fprintf (stderr, "#");
     }

     switch (ip_proto) {

Then, filtering is a piece of cake:

while (<CMD>) {
  s/($keyword)/\e[93m$1\e[0m/g;
  print;
}
raldi
A: 

This is easy in python.

#!/usr/bin/env python
import sys, re

keyword = 'RED'

while 1:
    c = sys.stdin.read(1)
    if not c:
        break
    if c in '#\n':
        sys.stdout.write(c)
    else:
        sys.stdout.write(
            (c+sys.stdin.readline()).replace(
            keyword, '\x1b[31m%s\x1b[0m\r' % keyword))
fivebells
This inhibits the instant appearance of hashmarks.
raldi
+3  A: 

You could also pipe the output through ack. The --passthru flag will help.

Andy Lester
A: 

See the script at this post to Linux-IL where someone asked a similar question. It's written in Perl and uses the CPAN Term::ANSIColor module.

Shlomi Fish
This inhibits the instant appearance of hashmarks.
raldi
+4  A: 

This seems to do the trick, at least comparing two windows, one running a straight ngrep (e.g. ngrep whatever) and one being piped into the following program (with ngrep whatever | ngrephl target-string).

#! /usr/bin/perl

use strict;
use warnings;

$| = 1; # autoflush on

my $keyword = shift or die "No pattern specified\n";
my $cache   = '';

while (read STDIN, my $ch, 1) {
    if ($ch eq '#') {
        $cache =~ s/($keyword)/\e[31m$1\e[0m/g;
        syswrite STDOUT, "$cache$ch";
        $cache = '';
    }
    else {
        $cache .= $ch;
    }
}
dland
Nice answer!It's worth noting this will treat $keyword as a Perl regexp, which is good if that's what you want. If we want to treat it as a literal string, then (\Q$keyword\E) in the match will do that, as will my $keyword = quotemeta(shift) or die ...
pjf
A: 

why not just call ngrep with the -q parameter to eliminate the hash marks?

Because I like the hash marks!
raldi