42

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?

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
AlwaysALearner
  • 6,320
  • 15
  • 44
  • 59

21 Answers21

80

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.

ghoti
  • 45,319
  • 8
  • 65
  • 104
  • 1
    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
  • 2
    @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
  • 2
    ghoti, 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
  • 1
    I'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
  • The function at the end will successfuly validate an empty string because $1 will be then substituted with '1.2.3.4'. If you want to copy this function into your own code, I suggest replacing '1.2.3.4' with e.g. 'IP_IS_NULL' – Andreas N Oct 11 '22 at 10:25
  • If one would copy the function at the end into his/her own code, the value '1.2.3.4' in `${1:-...}` should be replaced with something actually invalid (e.g. `IP_IS_NULL`), so that an empty string for `$1` would actually fail. – Andreas N Oct 11 '22 at 10:34
  • @AndreasN .. excellent suggestion, it's true that there are copy-n-pasters who fail to customize what they grab here. :) I've updated the final function, but left the earlier ones as they are more likely to be seen as examples. – ghoti Oct 12 '22 at 01:17
25

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
Neo
  • 351
  • 3
  • 5
12

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
  • 2
    Doesn'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." – rfmoz Sep 17 '18 at 07:58
  • 1
    if ipcalc -c $ip |grep -i invalid ;then echo "Provided IP $ip was invalid" ;fi – T'Saavik Sep 01 '20 at 17:54
  • 1
    Answer 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
7

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
}
Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
shannonman
  • 841
  • 4
  • 8
  • 2
    You 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
  • 1
    That'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
  • 1
    You 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
4

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
William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • 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
  • Actually this will bail in the test a.a.a.a – mcdoomington Dec 25 '17 at 20:41
  • @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
3

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
ghoti
  • 45,319
  • 8
  • 65
  • 104
DirectedSoul
  • 81
  • 10
3

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

Adam Bowen
  • 31
  • 1
2

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]?$)"
Neeraj
  • 33
  • 3
1

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
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • 1
    Nice. 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
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
zpangwin
  • 1,082
  • 1
  • 12
  • 17
1

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
1

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
mhck
  • 873
  • 1
  • 10
  • 20
0
#!/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"
TJW
  • 1
  • 1
0

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

Naresh Joshi
  • 887
  • 1
  • 7
  • 9
0

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.

zcoder
  • 42
  • 1
  • 2
  • 7
0

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
baponkar
  • 272
  • 4
  • 15
0
#!/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
  • 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
0

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
0

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
George Pavelka
  • 2,161
  • 11
  • 8
0

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
}
-1

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

ktaka
  • 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
  • @MaXi32 It will return 1 for invalid IP regardless of connection status. – ktaka Sep 17 '22 at 06:27