How do I check the validity of an IP address in a shell script, that is within the range 0.0.0.0 to 255.255.255.255?
- 25,191
- 47
- 61
- 93
- 5,890
- 14
- 40
- 55
-
You might try my suggestion posted here: https://unix.stackexchange.com/a/389565/249079 – Ganapathy Aug 31 '17 at 16:44
17 Answers
If you're using bash, you can do a simple regex match for the pattern, without validating the quads:
#!/usr/bin/env bash
ip=1.2.3.4
if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "success"
else
echo "fail"
fi
If you're stuck with a POSIX shell, then you can use expr to do basically the same thing, using BRE instead of ERE:
#!/bin/sh
ip=1.2.3.4
if expr "$ip" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; then
echo "success"
else
echo "fail"
fi
Note that expr assumes that your regex is anchored to the left-hand-side of the string, so the initial ^ is unnecessary.
If it's important to verify that each quad is less than 256, you'll obviously require more code:
#!/bin/sh
ip=${1:-1.2.3.4}
if expr "$ip" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; then
for i in 1 2 3 4; do
if [ $(echo "$ip" | cut -d. -f$i) -gt 255 ]; then
echo "fail ($ip)"
exit 1
fi
done
echo "success ($ip)"
exit 0
else
echo "fail ($ip)"
exit 1
fi
Or perhaps even with fewer pipes:
#!/bin/sh
ip=${1:-1.2.3.4}
if expr "$ip" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; then
IFS=.
set $ip
for quad in 1 2 3 4; do
if eval [ \$$quad -gt 255 ]; then
echo "fail ($ip)"
exit 1
fi
done
echo "success ($ip)"
exit 0
else
echo "fail ($ip)"
exit 1
fi
Or again, if your shell is bash, you could use a cumbersome regular expression for quad validation if you're not fond of arithmetic:
#!/usr/bin/env bash
ip=${1:-1.2.3.4}
re='^(0*(1?[0-9]{1,2}|2([0-4][0-9]|5[0-5]))\.){3}'
re+='0*(1?[0-9]{1,2}|2([0-4][0-9]|5[0-5]))$'
if [[ $ip =~ $re ]]; then
echo "success"
else
echo "fail"
fi
This could also be expressed in BRE, but that's more typing than I have in my fingers.
And lastly, if you like the idea of putting this functionality ... in a function:
#!/usr/bin/env bash
ip=${1:-1.2.3.4}
ipvalid() {
# Set up local variables
local ip=${1:-1.2.3.4}
local IFS=.; local -a a=($ip)
# Start with a regex format test
[[ $ip =~ ^[0-9]+(\.[0-9]+){3}$ ]] || return 1
# Test values of quads
local quad
for quad in {0..3}; do
[[ "${a[$quad]}" -gt 255 ]] && return 1
done
return 0
}
if ipvalid "$ip"; then
echo "success ($ip)"
exit 0
else
echo "fail ($ip)"
exit 1
fi
There are many ways you could do this. I've shown you just a few.
- 43,365
- 8
- 62
- 98
-
Nice solution. It works for me! I observed, however, using your `ipvalid` function that the input variable gets modified (e.g. 111.222.333.444 becomes 111 222 333 444 after the function gets called). I think it occurs at `local -a a=($ip)`. I observed it in bash on both macOS and Debian. – focorner Apr 08 '17 at 13:50
-
1@focorner, glad you like it. :) The input variable doesn't get modified, but the `IFS=.` causes it to be *expressed* differently. I suspect that the best solution here is to assign the field separator locally, so `local IFS=.;`, so that the change doesn't affect behaviour outside of the function. I've made this change in the script above; please let me know if it still shows the same behaviour for you. – ghoti Apr 10 '17 at 02:36
-
1ghoti, you're right: setting `IFS=.` as local inside the function as you suggested does the trick. Thank you. A+ – focorner Apr 10 '17 at 23:32
-
1I've changed `[[ $ip =~ ^[0-9]+(\.[0-9]+){3}$ ]] || return 1` with `[[ $ip =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || return 1`, to avoid 00200.0.0.1 be valid ( it actually valid, but not decimal) – MaximKostrikin Nov 20 '18 at 12:20
-
@MaximKostrikin, that's reasonable. Leading zeroes in a quad are subject to misinterpretation. What about `020` or `002` though? Would it not be better to go all the way, with `^[1-9][0-9]{,2}(\.[1-9][0-9]{,2}){3}$`? The logical extreme of this is of course mentioned earlier in the answer, with a RE that matches 0 to 255. I think my preferred option would be simply to use a `10#` at the start of the arithmetic comparison, to force use of base 10. If there are leading zeroes, then so be it. – ghoti Nov 20 '18 at 14:17
-
use a `10#` at the start of the arithmetic comparison is good, but how same string validated by the function will be interpreted by other tool - iptables, ip, ipset ? Also as decimal? – MaximKostrikin Nov 29 '18 at 06:03
-
@MaximKostrikin .. and that is precisely why you might want to consider a leading zero to be a format error. Different tools use different rules. `ipfw` and `route` read quads with leading zeros as octal. But an IPv4 address represented in dotted-decimal notation has by its very name decimal numbers in it, so I might argue that anything which interprets the quads as anything but decimal is Doing It Wrong. – ghoti Nov 29 '18 at 06:41
-
Simplest solution i found for bash. This should solve leading 0s issue. `[[ "${a[$quad]}" -gt 255 || "${a[$quad]}" =~ ^0[0-9] ]] && return 1` – kk. Apr 20 '21 at 09:31
This single regex should validate only those addresses between 0.0.0.0 and 255.255.255.255:
#!/bin/bash
ip="1.2.3.4"
if [[ "$ip" =~ ^(([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))\.){3}([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))$ ]]; then
echo "success"
else
echo "fail"
fi
- 161
- 1
- 4
-
It should be the accepted answer, the others can see "500.400.300.200" as valid IPv4 address. – Feriman Apr 04 '21 at 08:11
-
This is a better solution for me and more concise, the accepted answer returns triple and double zeros (eg: 000.00.000.00) as valid. – Morekid Dec 01 '21 at 10:13
-
Use ipcalc ( tested with the version package in RPM initscripts-9.49.49-1)
$ ipcalc -cs 10.10.10.257 && echo vaild_ip || echo invalid_ip
invalid_ip
- 574
- 7
- 9
-
2Doesn't work on my system (Debian Linux), `-s` parameter expects an argument and the return code appears to be 0 even when an error occurs. – Peter Gerber Mar 19 '17 at 12:22
-
Couldn't make it work on Cygwin because of the `-s` parameter, either. The answer "This single regex..." by @JonSouth a few posts below worked like a charm, though. – Keijo D Putt Feb 27 '18 at 21:19
-
Doesn't work. "-s" option is "Split into networks of size n1, n2, n3." – Rfraile Sep 17 '18 at 07:58
-
1if ipcalc -c $ip |grep -i invalid ;then echo "Provided IP $ip was invalid" ;fi – T'Saavik Sep 01 '20 at 17:54
-
1Answer from @T'Saavik does not work because the text `invalid` won't be found because it is a standard error output, should change to standard ouput to filter with grep – MaXi32 Jul 08 '21 at 10:39
The script Validating an IP Address in a Bash Script by Mitch Frazier does what you want to do:
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 }
- 45,811
- 27
- 120
- 136
- 851
- 4
- 8
-
2You beat me to it by a second. Also make sure to check the comments down the bottom, there are a few revisions to handle some cases. – PeterJ Dec 08 '12 at 12:23
-
1That's bash, not POSIX. The OP has specified that he's using linux, but not necessarily that bash is the shell. – ghoti Dec 08 '12 at 15:48
-
1You should edit the code into your post for both convenience and so your answer doesn't become invalid if the link goes down. – icktoofay Dec 08 '12 at 21:56
The typical solutions for this all seem to use regular expressions, but it occurs to me that it might be a better approach to do something like:
if echo "$ip" | { IFS=. read a b c d e;
test "$a" -ge 0 && test "$a" -le 255 &&
test "$b" -ge 0 && test "$b" -le 255 &&
test "$c" -ge 0 && test "$c" -le 255 &&
test "$d" -ge 0 && test "$d" -le 255 &&
test -z "$e"; }; then echo is valid; fi
- 190,037
- 45
- 260
- 285
-
Note that this fails for the string `1.2.3.4.` (note the trailing `.`) – William Pursell Dec 09 '12 at 03:21
-
It's not just trailing dots, this also gives a false result on `1.2.3.4.a`. Obviously in bash you could use a pattern match, perhaps even an extglob, to pre-test `$ip` before running through the code in your answer, but what might you do in POSIX? – ghoti Apr 19 '16 at 11:48
-
1@ghoti Makes a good point. We can make this more robust easily with a 5th variable that should be empty. (I am making no claims at all that this is reliable, but this edit is certainly better.) – William Pursell Apr 19 '16 at 13:46
-
Very nice. My only concern at this point would then be the unhandled (and perhaps unnecessary) output when `/bin/test` tries to evaluate non-integers using `-ge` or `-le`. But if someone cared, they could always redirect stderr. – ghoti Feb 07 '17 at 16:54
-
-
@mcdoomington. Not sure what you mean. If ip is the string a.a.a.a, this correctly detects that it is not a valid ip and emits an error message of the form "integer expression expected". If by "bail" you mean "perform correctly", then you're right. – William Pursell Dec 25 '17 at 20:44
-
True, by fault. You could do a Bash test for it being a digit: [[ "$a" == *[[:digit:]]* ]] & – mcdoomington Dec 26 '17 at 00:21
i tweaked all the codes and found this to be helpful.
#!/bin/bash
ip="256.10.10.100"
if [[ "$ip" =~ (([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5]))$ ]]; then
echo "success"
else
echo "fail"
fi
- 43,365
- 8
- 62
- 98
- 71
- 9
-
Accepting 1.2.3.4.5, 1.2.3.4.5.6, 255.255.255.255.255, etc. You need to limit number of octets. – Hasan Rumman Mar 30 '20 at 07:02
If someone still looking for an answer just by using regex, below would work -
echo "<sample ip address>"|egrep "(^[0-2][0-5]{1,2}?\.|^[3-9][0-9]?\.)([0-2][0-5]{1,2}?\.|[3-9][0-9]?\.)([0-2][0-5]{1,2}?\.|[3-9][0-9]?\.)([0-2][0-5]{1,2}?$|[3-9][0-9]?$)"
- 3,250
- 7
- 26
- 49
- 33
- 3
Perl has a great module Regexp::Common for validating various things:
perl -MRegexp::Common=net -e 'exit(shift() !~ /^$RE{net}{IPv4}$/)' $ipaddr
You may need to sudo cpan install Regexp::Common first
I'd wrap it in a function:
valid_ip() {
perl -MRegexp::Common=net -e 'exit(shift() !~ /^$RE{net}{IPv4}$/)' "$1"
}
if valid_ip 123.234.345.456; then
echo OK
else
echo INVALID
fi
- 223,850
- 36
- 205
- 328
-
1Nice. Though, I wouldn't be recommending direct CPAN installs for most folks. If modules can be installed from a package repository, you'll generally keep your system cleaner and more secure. In Ubuntu, you would `sudo apt-get install libregexp-common-perl` In FreeBSD, it would be `cd /usr/ports/textproc/p5-Regexp-Common && make install`. – ghoti Dec 09 '12 at 02:32
I prefer to use ipcalc to do this, as long as my script doesn't have to be portable.
ipcalc 1.1.1.355
INVALID ADDRESS: 1.1.1.355
Address: 192.168.1.1 11000000.10101000.00000001. 00000001
Netmask: 255.255.255.0 = 24 11111111.11111111.11111111. 00000000
Wildcard: 0.0.0.255 00000000.00000000.00000000. 11111111
=>
Network: 192.168.1.0/24 11000000.10101000.00000001. 00000000
HostMin: 192.168.1.1 11000000.10101000.00000001. 00000001
HostMax: 192.168.1.254 11000000.10101000.00000001. 11111110
Broadcast: 192.168.1.255 11000000.10101000.00000001. 11111111
Hosts/Net: 254 Class C, Private Internet
There is a great page showing how to use it in scripting, etc, here: SleeplessBeastie's Notes
- 11
- 1
Alternate version that still does a thorough validation (meaning that it requires both a properly formatted IP address AND that each quadrant is within the range of allowed values aka 0-255). Works fine on GNU bash 4.4.20 (Linux Mint 19.3); no promises elsewhere but will prolly be fine as long as you have bash 4.
The initial format check regex is borrowed from the shannonman / Mitch Frazier answer above; the rest is my own.
function isValidIpAddr() {
# return code only version
local ipaddr="$1";
[[ ! $ipaddr =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] && return 1;
for quad in $(echo "${ipaddr//./ }"); do
(( $quad >= 0 && $quad <= 255 )) && continue;
return 1;
done
}
function validateIpAddr() {
# return code + output version
local ipaddr="$1";
local errmsg="ERROR: $1 is not a valid IP address";
[[ ! $ipaddr =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] && echo "$errmsg" && return 1;
for quad in $(echo "${ipaddr//./ }"); do
(( $quad >= 0 && $quad <= 255 )) && continue;
echo "$errmsg";
return 1;
done
echo "SUCCESS: $1 is a valid IP address";
}
$ isValidIpAddr '192.168.0.1'
$ echo "$?"
0
$ isValidIpAddr '192.168.0.256'
$ echo "$?"
1
$ validateIpAddr '12.1.10.191'
SUCCESS: 12.1.10.191 is a valid IP address
$ validateIpAddr '1.1.1.127'
SUCCESS: 1.1.1.127 is a valid IP address
$ validateIpAddr '1.1.1.1337'
ERROR: 1.1.1.1337 is not a valid IP address
- 913
- 1
- 9
- 14
We can use "ip route save" to do the check.
valid_addrmask()
{
ip -4 route save match $1 > /dev/null 2>&1
}
$ valid_addrmask 255.255.255.255 && echo "is valid" || echo "is not valid"
is valid
$ valid_addrmask 255.255.255.355 && echo "is valid" || echo "is not valid"
is not valid
- 11
- 1
#!/bin/bash
read -p " ip: " req_ipadr
#
ip_full=$(echo $req_ipadr | sed -n 's/^\(\(\([1-9][0-9]\?\|[1][0-9]\{0,2\}\|[2][0-4][0-9]\|[2][5][0-4]\)\.\)\{3\}\([1-9][0-9]\?\|[1][0-9]\{0,2\}\|[2][0-4][0-9]\|[2][5][0-4]\)\)$/\1/p')
#
[ "$ip_full" != "" ] && echo "$req_ipadr vaild ip" || echo "$req_ipadr invaild ip"
- 1
- 1
You can just copy the following code and change body of if else control as per your need
function checkIP(){
echo "Checking IP Integrity"
ip=$1
byte1=`echo "$ip"|xargs|cut -d "." -f1`
byte2=`echo "$ip"|xargs|cut -d "." -f2`
byte3=`echo "$ip"|xargs|cut -d "." -f3`
byte4=`echo "$ip"|xargs|cut -d "." -f4`
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ && $byte1 -ge 0 && $byte1 -le 255 && $byte2 -ge 0 && $byte2 -le 255 && $byte3 -ge 0 && $byte3 -le 255 && $byte4 -ge 0 && $byte4 -le 255 ]]
then
echo "IP is correct"
else
echo "This Doesn't look like a valid IP Address : $ip"
fi
}
checkIP $myIP
Calling the method with IP Address stored in a variable named myIP.
$ip =~ ^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$ - This part makes sure that IP consists of 4 blocks separated by a dot(.) but every block here is allowed to range from 0 - 999
Since desired range of every block would be 0 - 255, to make sure of that below line can be used.
$byte1 -ge 0 && $byte1 -le 255 && $byte2 -ge 0 && $byte2 -le 255 && $byte3 -ge 0 && $byte3 -le 255 && $byte4 -ge 0 && $byte4 -le 255
- 737
- 5
- 7
In the most simple form:-
#!/bin/bash
while true;
do
read -p "Enter a ip: " IP
echo "${IP}" > ip.txt
OCT1=$(cat ip.txt | awk -F "." '{print $1}')
OCT2=$(cat ip.txt | awk -F "." '{print $2}')
OCT3=$(cat ip.txt | awk -F "." '{print $3}')
OCT4=$(cat ip.txt | awk -F "." '{print $4}')
REGEX_IP='^[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}$'
if [[ ${IP} =~ ${REGEX_IP} ]]
then
if [[ ${OCT1} -gt 255 || ${OCT2} -gt 255 || ${OCT3} -gt 255 || ${OCT4} -gt 255 ]]
then
echo "Please enter a valid ip"
continue
fi
break
else
echo "Please enter a valid ip"
continue
fi
done
This will cover all the scenarios.
- 42
- 1
- 6
May be it is usefull
#this script verify either a ip address is valid or not as well as public or local ip
#$1 means supplied first argument
ip=$(echo $1 | gawk '/^[0-9]{1,3}\.[0-9]{1,3}+\.[0-9]{1,3}+\.[0-9]{1,3}$/{print $0}')
#regular expression to match pattarn from 0.0.0.0 to 999.999.999.999 address
ip1=$(echo $ip | gawk -F. '{print $1}')
ip2=$(echo $ip | gawk -F. '{print $2}')
ip3=$(echo $ip | gawk -F. '{print $3}')
ip4=$(echo $ip | gawk -F. '{print $4}')
echo "Your ip is : $ip1.$ip2.$ip3.$ip4" #extract four number from the address
#To rectify original ip range 0-255
if [[ $ip1 -le 255 && $ip1 -ne 0 && $ip2 -ne 0 && $ip2 -le 255 && $ip3 -ne 0 && $ip3 -le 255 && $ip4 -ne 0 && $ip4 -le 255 ]]
then
echo "This is a valid ip address"
else
echo "This is not a valid ip address"
fi
if [[ $ip1 -eq 198 ]]
then
echo "It may be a local ip address"
else
echo "It may be a public ip address"
fi
- 212
- 4
- 14
#!/bin/bash
IP="172.200.22.33.88"
p=`echo $IP | tr '.' '\n' | wc -l`
echo $p
IFS=.
set $IP
echo $IP
a=$1
b=$2
c=$3
d=$4
if [[ $p == 4 && $a -lt 255 && $b -lt 255 && $c -lt 255 && $d -lt 255 ]]
then
echo " THIS is Valid IP "
else
echo "THIS IS NOT VALID IP ADDRESS"
fi
- 31
- 2
-
Your answer could be improved with additional supporting information. Please [edit](https://stackoverflow.com/posts/30864749/edit) to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](https://stackoverflow.com/help/how-to-answer). – Panagiotis Simakis Jan 23 '22 at 23:56
How about this?
# ip route get 10.10.10.100 > /dev/null 2>&1 ; echo $?
0
# ip route get 10.10.10.300 > /dev/null 2>&1 ; echo $?
1
Since the "ip" command checks the validity of IP in itself.
- 7
- 2
-
I don't recommend this. This required that the IP is reachable (u need internet connection). So it will return 1 if u disconnect the internet. – MaXi32 Sep 21 '21 at 01:29