Find all occurances in a file with sed

grep will do a better job of this:

grep -B 3 141.299.99.1 TESTFILE

The -B 3 means to print the three lines before each match. This will print -- between each group of lines. To disable that, use --no-group-separator as well.

The -B option is supported by GNU grep and most BSD versions as well (OSX, FreeBSD, OpenBSD, NetBSD), but it is technically not a standard option.


With sed you can do a sliding window.

sed '1N;$!N;/141.299.99.1/P;D'

That does it. But beware - bash's insane behavior of expanding ! even when quoted!!! into the command string from your command history might make it go a little crazy. Prefix the command with set +H;if you find this is the case. To then re-enable it (but why???) do set -H afterward.

That, of course, would only apply if you were using bash - though I don't believe you are. I'm fairly certain you're working with csh - (which happens to be the shell whose insane behavior bash emulates with the history expansion, but maybe not to the extremes the c shell took it). So probably a \! should work. I hope.

It's all portable code: POSIX describes its three operators thus: (though it's worth noting that I've only confirmed this description existed as early as 2001)

[2addr]N Append the next line of input, less its terminating \newline, to the pattern space, using an embedded \newline to separate the appended material from the original material. Note that the current line number changes.

[2addr]P Write the pattern space, up to the first \newline, to standard output.

[2addr]D Delete the initial segment of the pattern space through the first \newline and start the next cycle.

So on the first line you add an extra line to pattern space, so it looks like this:

^line 1s contents\nline 2s contents$

Then on the first line and every line thereafter - excepting the very last - you add another line to pattern space. So it looks like this:

^line 1\nline 2\nline 3$

If your ip address is found within you Print up to the first newline, so just line 1 here. At the end of every cycle you Delete same and start over with what remains. So the next cycle looks like:

^line 2\nline 3\nline 4$

...and so on. If your ip is to be found on any one of those three the oldest will print out - every time. So you're always only three lines ahead.

Here's a quick example. I'll get a three line buffer printed for every number ending in zero:

seq 10 52 | sed '1N;$!N;/0\(\n\|$\)/P;D'

10
18
19
20
28
29
30
38
39
40
48
49
50

That one's a little more complicated than your case because I had to alternate from either 0\n newline or 0$ end of pattern space to more closely resemble your problem - but they are subtly different in that this requires an anchor - which can be a little difficult to do since pattern-space constantly shifts.

I used the odd cases of 10 and 52 to show that as long as the anchor is flexible then so is the output. Fully portably, I can achieve the same results by instead counting on the algorithm and do:

seq 10 52 | sed '1N;$!N;/[90]\n/P;D'

And widen the search while restricting my window - from 0 to 9 and 0 and from 3 lines to two.

Anyway, you get the idea.


Since you mention that you don't have the -B option to grep, you can use Perl (for example) to make a sliding a window of 4 lines:

perl -ne '
    push @window,$_;
    shift @window if @window > 4;
    print @window if /141\.299\.99\.1/
' your_file

Ramesh's answer does a similar thing with awk.

Tags:

Sed