AlwaysALearner
AlwaysALearner

Reputation: 6690

Check for IP validity

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

Answers (22)

Mike
Mike

Reputation: 9

First approach

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

Second approach

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).

Third approach

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

mhck
mhck

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

don-coleman
don-coleman

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

George Pavelka
George Pavelka

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

ghoti
ghoti

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

ktaka
ktaka

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

charlycode
charlycode

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

Uzzal Basak
Uzzal Basak

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

linuxsmiths
linuxsmiths

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

Nasimuddin Ansari
Nasimuddin Ansari

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

baponkar
baponkar

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

Neeraj
Neeraj

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

zpangwin
zpangwin

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

zcoder
zcoder

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

shannonman
shannonman

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

Naresh Joshi
Naresh Joshi

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

Adam Bowen
Adam Bowen

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

William Pursell
William Pursell

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

DirectedSoul
DirectedSoul

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

TJW
TJW

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

Neo
Neo

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

glenn jackman
glenn jackman

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

Related Questions