Validating an IP Address in a Bash Script
I've recently written about using bash arrays and bash regular expressions, so here's a more useful example of using them to test IP addresses for validity.
To belabor the obvious: IP addresses are 32 bit values written as four numbers (the individual bytes of the IP address) separated by dots (periods). Each of the four numbers has a valid range of 0 to 255.
The following bash script contains a bash function which returns true if it is passed a valid IP address and false otherwise. In bash speak true means it exits with a zero status, anything else is false. The status of a command/function is stored in the bash variable "$?".
#!/bin/bash
# Test an IP address for validity:
# Usage:
# valid_ip IP_ADDRESS
# if [[ $? -eq 0 ]]; then echo good; else echo bad; fi
# OR
# if valid_ip IP_ADDRESS; then echo good; else echo bad; fi
#
function valid_ip()
{
local ip=$1
local stat=1
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
OIFS=$IFS
IFS='.'
ip=($ip)
IFS=$OIFS
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
&& ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
stat=$?
fi
return $stat
}
# If run directly, execute some tests.
if [[ "$(basename $0 .sh)" == 'valid_ip' ]]; then
ips='
4.2.2.2
a.b.c.d
192.168.1.1
0.0.0.0
255.255.255.255
255.255.255.256
192.168.0.1
192.168.0
1234.123.123.123
'
for ip in $ips
do
if valid_ip $ip; then stat='good'; else stat='bad'; fi
printf "%-20s: %s\n" "$ip" "$stat"
done
fi
If you save this script as "valid_ip.sh" and then run it directly it will run some tests and prints the results:
# sh valid_ip.sh 4.2.2.2 : good a.b.c.d : bad 192.168.1.1 : good 0.0.0.0 : good 255.255.255.255 : good 255.255.255.256 : bad 192.168.0.1 : good 192.168.0 : bad 1234.123.123.123 : bad
In the function valid_ip, the if statement uses a regular expression to make sure the subject IP address consists of four dot separated numbers:
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
If that test passes then the code inside the if statement
separates the subject IP address into four parts at the dots and places the parts in an array:
OIFS=$IFS IFS='.' ip=($ip) IFS=$OIFSIt does this by momentarily changing bash's Internal Field Separator variable so that rather than parsing words as whitespace separated items, bash parses them as dot separated. Putting the value of the subject IP address inside parenthesis and assigning it to itself thereby turns it into an array where each dot separated number is assigned to an array slot. Now the individual pieces are tested to make sure they're all less than or equal to 255 and the status of the test is saved so that it can be returned to the caller:
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
&& ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
stat=$?
Note that there's no need to test that the numbers are greater than or equal to zero
because the regular expression test has already eliminated any thing that doesn't
consist of only dots and digits.
Mitch Frazier is an Associate Editor for Linux Journal.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.
Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.
Sponsored by ActiveState
| Non-Linux FOSS: libnotify, OS X Style | Jun 18, 2013 |
| Containers—Not Virtual Machines—Are the Future Cloud | Jun 17, 2013 |
| Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer | Jun 12, 2013 |
| Weechat, Irssi's Little Brother | Jun 11, 2013 |
| One Tail Just Isn't Enough | Jun 07, 2013 |
| Introduction to MapReduce with Hadoop on Linux | Jun 05, 2013 |
- Containers—Not Virtual Machines—Are the Future Cloud
- Non-Linux FOSS: libnotify, OS X Style
- Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer
- Linux Systems Administrator
- Validate an E-Mail Address with PHP, the Right Way
- Introduction to MapReduce with Hadoop on Linux
- RSS Feeds
- Weechat, Irssi's Little Brother
- New Products
- Developer Poll
- Reply to comment | Linux Journal
1 hour 39 min ago - Reply to comment | Linux Journal
2 hours 24 min ago - Didn't read
2 hours 35 min ago - Reply to comment | Linux Journal
2 hours 40 min ago - Poul-Henning Kamp: welcome to
4 hours 50 min ago - This has already been done
4 hours 51 min ago - Reply to comment | Linux Journal
5 hours 36 min ago - Welcome to 1998
6 hours 24 min ago - notifier shortcomings
6 hours 48 min ago - heroku?
8 hours 25 min ago
Featured Jobs
| Linux Systems Administrator | Houston and Austin, Texas | Host Gator |
| Senior Perl Developer | Austin, Texas | Host Gator |
| Technical Support Rep | Houston and Austin, Texas | Host Gator |
| UX Designer | Austin, Texas | Host Gator |
| Web & UI Developer (JavaScript & j Query) | Austin, Texas | Host Gator |
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?



Comments
More easy
#! /bin/sh
valid_ip()
{
# Check if IP format is num.num.num.num / num between 0..255
if [ "$(sipcalc $1 | grep ERR)" != "" ]; then
echo "incorrect"
return 0
fi
echo "correct"
}
Regards
3 octet ip addresses are passing as valid
On some of my machines (Centos 5.2, Debian Etch), it looks like 3 octet ip addresses are passing as valid:
(bill@shady) ~ > bash --version|head -1; head -1 /etc/issue.net; ./valid_ip.sh
GNU bash, version 3.1.17(1)-release (i486-pc-linux-gnu)
Debian GNU/Linux 4.0
4.2.2.2 : good
a.b.c.d : bad
192.168.1.1 : good
0.0.0.0 : good
255.255.255.255 : good
255.255.255.256 : bad
192.168.0.1 : good
192.168.0 : good
1234.123.123.123 : bad
(bill@spdb222) ~ > bash --version|head -1; head -1 /etc/issue.net; ./valid_ip.sh
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
CentOS release 5.2 (Final)
4.2.2.2 : good
a.b.c.d : bad
192.168.1.1 : good
0.0.0.0 : good
255.255.255.255 : good
255.255.255.256 : bad
192.168.0.1 : good
192.168.0 : good
1234.123.123.123 : bad
However, if I test on MacOSX, it works correctly:
(bill@quell) ~ > bash --version|head -1; uname -a; ./valid_ip.sh
GNU bash, version 3.2.17(1)-release (i386-apple-darwin9.0)
Darwin quell.local 9.5.0 Darwin Kernel Version 9.5.0: Wed Sep 3 11:29:43 PDT 2008; root:xnu-1228.7.58~1/RELEASE_I386 i386 i386
4.2.2.2 : good
a.b.c.d : bad
192.168.1.1 : good
0.0.0.0 : good
255.255.255.255 : good
255.255.255.256 : bad
192.168.0.1 : good
192.168.0 : bad
1234.123.123.123 : bad
Any ideas why this is?
A Late "Fix"
In version 3.2 of Bash there was a change that eliminated the need to quote regular expressions, it appears to be related to that.
If you put quotes around the regular expression test it should work:
if [[ $ip =~ '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$' ]]; thenORif [[ $ip =~ "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" ]]; thenNow as to why it fails in the manner it fails, I'm not sure. I also don't understand why the CentOS version fails since it's version 3.2.25.
While researching this I also ran across this statment here:
Which appears to indicate that you need to escape curly brackets for them to work, but that does not appear to be the case. I think the problem may be that even though the above link appears as part of the Advanced Bash-Scripting Guide, it looks like it may have been largely copied and pasted from another source.
Mitch Frazier is an Associate Editor for Linux Journal.
Not the bash I know
I tried this with bash, since I wasn't aware that bash supported regular expressions, and it fell over when it encountered '=~', since it didn't know wnything about regular expressions.
What version of bash was this written with?
Version 3
Regular expressions have been part of bash since version 3 arrived on July 27, 2004. Specifically, I have version 3.1.17.
Mitch Frazier is an Associate Editor for Linux Journal.
Re: Validating an IP Address
Actually an IP address doesn't have to be in the dotted quad format to work correctly, it depends on where you use it:
$ perl -MSocket -le'print inet_ntoa inet_aton q/www.linuxjournal.com/'
66.240.243.113
$ perl -MSocket -le'print unpack q/N/, inet_aton q/66.240.243.113/'
1123087217
$ ping 1123087217
PING 1123087217 (66.240.243.113) 56(84) bytes of data.
64 bytes from 66.240.243.113: icmp_seq=1 ttl=52 time=72.6 ms
64 bytes from 66.240.243.113: icmp_seq=2 ttl=52 time=66.7 ms
64 bytes from 66.240.243.113: icmp_seq=3 ttl=52 time=66.7 ms
--- 1123087217 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 66.702/68.711/72.667/2.813 ms
$ traceroute 1123087217
traceroute to 1123087217 (66.240.243.113), 30 hops max, 40 byte packets
1 * * *
2 ... etc.
$ perl -MSocket -le'print unpack q/N/, inet_aton q/0.240.243.113/'
15790961
$ ping 66.15790961
PING 66.15790961 (66.240.243.113) 56(84) bytes of data.
64 bytes from 66.240.243.113: icmp_seq=1 ttl=52 time=104 ms
64 bytes from 66.240.243.113: icmp_seq=2 ttl=52 time=99.8 ms
--- 66.15790961 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 99.822/102.097/104.372/2.275 ms
$ perl -MSocket -le'print unpack q/N/, inet_aton q/0.0.243.113/'
62321
$ ping 66.240.62321
PING 66.240.62321 (66.240.243.113) 56(84) bytes of data.
64 bytes from 66.240.243.113: icmp_seq=1 ttl=52 time=65.0 ms
64 bytes from 66.240.243.113: icmp_seq=2 ttl=52 time=93.5 ms
64 bytes from 66.240.243.113: icmp_seq=3 ttl=52 time=66.0 ms
--- 66.240.62321 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 65.090/74.920/93.578/13.202 ms
Dash Support
Good but does not run using dash!!
I don't think dash supports the regex in compound expressions and doesn't support the function keyword and [[]] parenthesis.
Probably not
Only tested it with bash, nothing else.
Mitch Frazier is an Associate Editor for Linux Journal.
What about IPv6 addresses?
This does not handle IPv6 addresses which is a much more trickier gambit. And a combination of IPv4/ IPv6 addresses would indeed make things interesting. Add the mundane alphanumeric DNS names and you have a real stew of messy, unpredicatable code! ;-)
As They Say
That's left as an exercise for the reader.
Mitch Frazier is an Associate Editor for Linux Journal.