Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm trying to figure out how to use grep to pull out only the first two numbers of an IP address in a list. I've got a routine that is isolating the log entires (from Exim) that I need, now I just need to determine what the first two IP address numbers are.

I've tried this:

unique_ips2=$(grep "$email" tmp_exim|grep -oE "[0-9]{1,3}\.[0-9]{1,3}"|sort -u)

But instead of just getting the first two numbers in a 4 IP set, this is giving me 8 pairs of numbers, i.e the first two and the last two of each IP.

The original statement uses sed to pull out the complete IPs, like so:

unique_ips=$(grep "$email" tmp_exim|sed 's/.*[[]\([0-9\.]*\)[]].*/\1/'|sort -u|wc -l)

But I don't have any familiarity with sed at all, and have no idea how to modify this statement as every attempt has only caused errors.

share|improve this question
Does tmp_exim just contain IP addresses line per line? If not, please show us an example content. – scai Feb 28 '14 at 8:12

For a plain list of IPv4 addresses a simple cut should be enough:

cut -d "." -f -2 tmp_exim | sort -u

share|improve this answer

Exim log files contain lots of data on each line. Continuing on with the OP's approach for separating out the IP information, consider:

unique_ips2=$(grep "$email" tmp_exim | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.' | sort -u)

The first change above is to put the regex inside single-quotes so that bash doesn't mess with it. The second change is escaping the period. Unescaped, period means any character but we want it to mean only a literal period. The third change is to add a (literal) period at the end of the regex. This prevents the regex from matching the last two numbers of an IP address.

sed can also do this well:

unique_ips2=$(sed -nr "/$email/"' s/.*\[([0-9]{1,3}\.[0-9]{1,3})\..*/\1/p' tmp_exim | sort -u)

The option -n tells sed not to print anything unless we explicitly ask it to and -r tells it, for convenience, to use extended regex. (OSX is different: replace-r with -E.) The expression /$email/ tells sed to operate only on lines that have that email address in them. (When using sed, grep is often superfluous.) The meat of the command is the substitution s/.*\[([0-9]{1,3}\.[0-9]{1,3})\..*/\1/p. This looks for any text at all (.*) followed by a literal open square bracket (in my exim logs, the IP address is always preceded by an open square bracket) follow by a number, a period, another number, a period, and then anything at all (.*). The number-period-number regex is grouped inside parentheses. This means that we can refer to it as \1 (it is the only parens grouping so it is also the first). So, the substitute command replaces the whole line with just the first two numbers of the IP address. The final p in the command tells sed to print the result if the substitution occurs.

share|improve this answer
Excellent. Thanks very much. We ended up inserting the longer sed line suggested above, and now I am working this into the overall detection script. – jols Mar 1 '14 at 5:55

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.