Reputation: 6690
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
?
Upvotes: 49
Views: 108324
Reputation: 9
To check if an IPV4 is valid in a POSIX shell i'm using it :
#!/bin/sh
LANG=C
TMP="$1" # <-- your string in input
IP="$TMP"
CHECK=""
I=4
while [ $I -ne 0 ]; do
I=$(( $I - 1 ))
J="${TMP%%.*}"
K="${J#[0-9]}"
K="${K#[0-9]}"
K="${K#[0-9]}"
if [ "$J" != "$K" ] && [ -z "$K" ] && [ "$J" -ge 0 ] && [ "$J" -le 255 ]; then
CHECK="$CHECK.$(( $J ))"
fi
TMP="${TMP#*.}"
done
CHECK="${CHECK#.}"
if [ -n "$IP" ] && [ "$IP" = "$CHECK" ]; then
echo "'$IP' is valid" # input is from 0.0.0.0 to 255.255.255.255
else
echo "'$IP' is not valid" # input isn't a valid IP
fi
Example when this script is located in ./ip-check
:
paris@pro:~# ./ip-check 1.1.1.255
'1.1.1.255' is valid
paris@pro:~# ./ip-check 1.1.1.256
'1.1.1.256' is not valid
paris@pro:~# ./ip-check 1.01.1.1
'1.01.1.1' is not valid
paris@pro:~# ./ip-check demo
'demo' is not valid
Another variation of this method uses a function and 6 return statements to show you which case occurred :
#!/bin/sh
is_valid_ip4() {
IP="$1"
LEN=${#IP}
if [ $LEN -lt 7 ] || [ $LEN -gt 15 ] ; then
return 1 # excepting an IPv4 compatible length
fi
I=0
while [ $I -lt 4 ]; do
N="${IP%%.*}"
I=$(( 1 + $I ))
if [ -n "${N#[1-2][0-9][0-9]}" ] && [ -n "${N#[1-9][0-9]}" ] && [ -n "${N#[0-9]}" ]; then
return 2 # excepting 1, 2 or 3 digits
elif [ $N -lt 0 ] || [ $N -gt 255 ]; then
return 3 # excepting a number between 0 and 255
fi
if [ $I -eq 4 ]; then
[ "$IP" != "$N" ] && return 4
else
[ "$IP" = "${IP#*.}" ] && return 5
IP="${IP#*.}" # updating the checked string
fi
done
return 0 # input was valid
}
is_valid_ip4 $1 # check the first parameter that the script received
RES=$?
if [ $RES -eq 0 ]; then
echo "'$1' is a valid IPV4"
else
echo "'$1' is not a valid IPV4 (case $RES)"
fi
Example when this script is located in ./ip-check
:
paris@pro:~# ./ip_check length
'length' is not a valid IPV4 (case 1)
paris@pro:~# ./ip_check 1.1.1_1
'1.1.1_1' is not a valid IPV4 (case 2)
paris@pro:~# ./ip_check 1.1.1.256
'1.1.1.256' is not a valid IPV4 (case 3)
paris@pro:~# ./ip_check 1.1.1.1. ;
'1.1.1.1.' is not a valid IPV4 (case 4)
paris@pro:~# ./ip_check 111.111 ;
'111.111' is not a valid IPV4 (case 5)
paris@pro:~# ./ip_check 1.1.1.1 ;
'1.1.1.1' is a valid IPV4
This POSIX compatible method does not require a pipe, redirection, or additional compilation (unlike an expr regular expression).
The third approach, just as simple, use IFS and case/esac :
#!/bin/sh
valid_ip4() {
local IP="$1" IFS="." NUM
set -- $IP
[ "$#" != 4 ] && return 1 # it takes 4 parts
for NUM; do
case "$NUM" in
*[!0-9]*) return 2 # we except only digits
esac
[ "$NUM" != "$(( NUM ))" ] && return 3 # we except a regular integer
[ "$NUM" -gt 255 ] && return 4 # we except a number under 256
done
return 0 # a valid ipv4 was submited
}
valid_ip4 $1 # checking for the first parameter
RES=$?
if [ $RES -eq 0 ]; then
echo "'$1' is a valid IPV4"
else
echo "'$1' is not a valid IPV4 (case $RES)"
fi
These approaches should be fast and accurate.
Upvotes: 0
Reputation: 1051
I like the answer posted by Neo.
For clarity, I would add a variable for the duplicate portion of the regex.
#!/bin/bash
ip="1.2.3.4"
regex0to255='([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))'
if [[ "${ip}" =~ ^(${regex0to255}\.){3}${regex0to255}$ ]]; then
echo "success"
else
echo "fail"
fi
Upvotes: 1
Reputation: 1
I use the following on my router, running the Ash shell. This scripts has a very small footprint, as it only uses builtin commands, and no forking or subshells. It implements a checkIP() function, that returns false if the IP is invalid, and true if valid.
#
# basic validation on the IPv4 address
checkIPv4()
{
local IP="$1"
local N
local OIFS
# only numbers and dots in the entire IP address, no empty quads, and no
# leading or trailing dots
case "${IP}" in
*[!0-9.]* | *..* | .* | *. ) #
return 1
;;
esac
OIFS="${IFS}"
IFS=.
set -- $IP
IFS="${OIFS}"
if [ $# -ne 4 ]; then
return 1
fi
for N in "$@"; do
if [ "${#N}" -lt 1 -o "${#N}" -gt 3 ]; then
return 1
fi
# at this point, we are guaranteed it is a positive number
# of reasonable length
if [ "$N" -gt 255 ]; then
return 1
fi
done
return 0
}
Upvotes: 0
Reputation: 2329
Check out my solution if you like it. Simple, readable, no extra variables.
function valid_ip () {
[[ ${1} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] \
|| return 1
for i in ${1//./ }; do
[[ ${i} -le 255 ]] \
|| return 1
done
}
Usage:
ips='192.168.1.1 192.168.1.333
8.8.8.8 8.8.8 a.b.c.d blabla'
for ip in ${ips}; do
valid_ip "${ip}" \
&& echo "${ip} is valid" \
|| echo "${ip} is INVALID"
done
Output:
192.168.1.1 is valid
192.168.1.333 is INVALID
8.8.8.8 is valid
8.8.8 is INVALID
a.b.c.d is INVALID
blabla is INVALID
Upvotes: 0
Reputation: 46816
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:-NO_IP_PROVIDED}
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.
Upvotes: 91
Reputation: 7
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.
(2022/9/17) When the IP is not reachable i.e. network interface is down,
$ ip route get 10.10.10.100 > /dev/null 2>&1 ; echo $?
2
$ ip route get 10.10.10.300 > /dev/null 2>&1 ; echo $?
1
This means one can still distinguish if the IP is valid or not.
However, a better solution would be to write a small program, for example using inet_pton.
My comment in another thread, https://unix.stackexchange.com/a/581081/65646
Upvotes: -1
Reputation: 1
Validating IPv4 if is local
valid_ip(){
local ip=$IP
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
if [[ "$stat" = "0" ]];
then
echo "IPv4 Valid"
if [[ "${ip[0]}" = 192 || "${ip[0]}" = 10 || "${ip[0]}" = 172 ]];
then
echo "IPv4 is local"
stat=1
fi
else
echo "IPv4 not valid"
fi
return $stat
}
IP=10.10.10.1
valid_ip
Upvotes: 0
Reputation: 31
#!/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
Upvotes: 0
Reputation: 11
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
Upvotes: 1
Reputation: 647
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
Upvotes: 13
Reputation: 426
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
Upvotes: 0
Reputation: 53
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]?$)"
Upvotes: 3
Reputation: 1317
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
Upvotes: 2
Reputation: 72
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.
Upvotes: 0
Reputation: 861
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 }
Upvotes: 8
Reputation: 1033
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
Upvotes: 0
Reputation: 41
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
Upvotes: 4
Reputation: 212178
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
Upvotes: 5
Reputation: 91
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
Upvotes: 4
Reputation: 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"
Upvotes: 0
Reputation: 391
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
Upvotes: 29
Reputation: 246744
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
Upvotes: 1