0

I am trying to validate an ip address within a dash script. I've found many ways to achieve the same with bash such as in linuxjournal

Basically what is does is a comparision using this:

if [[ $ip =~ '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$' ]]; then
  do something
fi

Is there any way to get the same with dash?

UPDATE: This is the final script that does what I needed:

#In case RANGE is a network range (cidr notation) it's splitted to every ip from 
# that range, otherwise we just keep the ip
if echo $RANGE | grep -E -q '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}$'; then
    IPS=`prips $RANGE -e ...0,255`
    if [ "$?" != "0" ] ; then
        echo "ERROR: Not a valid network range!"
        exit 1
    fi
elif echo $RANGE | grep -E -q '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'; then
    IPS="$RANGE"
else
    echo "$RANGE no is not a valid IP address or network range"
    exit 1
fi
aseques
  • 537
  • 4
  • 21
  • An IP address is just a 32-bit number. You're asking for dotted-quad notation, but remember that decimal and hex notation are just as valid. `0x1020304` and `16909060` are the same as `1.2.3.4`. – ghoti Feb 14 '12 at 12:26
  • I only need validation for human error, no one other than me and my coleagues will use this script, so I can choose a notation and stick to that. – aseques Feb 14 '12 at 13:18
  • With regard to your final regexp, you might also represent it as something a bit shorter: `^([0-9]{1,3}\.){3}[0-9]{1,3}\/[0-9]{1,2}$` – ghoti Feb 15 '12 at 18:39
  • You're using a bunch of different external commands, while it is quite possible to do this nice and cleanly using only `dash` builtins. (See my response below if you're curious as to how.) – zrajm Jul 22 '14 at 23:59

3 Answers3

1

You can build a case statement, although it will be more verbose than a regex. On the other hand, you avoid spawning any external processes, and it might be easier to read and maintain to boot.

if case $ip in
    # Invalid if it contains any character other than number or dot
    # Invalid if it contains more than three dots
    # Invalid if it contains two adjacent dots
    # Invalid if it begins with a non-number
    # Invalid if it ends with a non-number
    *[!.0-9]* | *.*.*.*.* | *..* | [!0-9]* | *[!0-9] ) false ;;
    # Invalid if it contains a number bigger than 255:
    #  256-259 or 260-299 or 300-999 or four adjacent digits
    *25[6-9]* | *2[6-9][0-9]* | *[3-9][0-9][0-9]* | *[0-9][0-9][0-9][0-9]* ) false;;
    # Verify that it contains three dots
    *.*.*.* ) true ;;
    # Failing that, invalid after all (too few dots)
    *) false ;;
esac; then
    echo "$ip" is valid
fi

Notice the funky use of a case statement (returning either true or false) as the condition in an if statement.

This is slightly stricter than the regex in that it requires each octet to be less than 256.

tripleee
  • 175,061
  • 34
  • 275
  • 318
0

Here is a small dash shell function which does not use any external commands and that checks if an IPv4 address is valid. It returns true if the address was correctly formatted, false otherwise.

I've tried to explain the magic in my comments.

valid_ip() {
    local IP="$1" IFS="." PART           # make vars local, get arg, set $IFS
    set -- $IP                           # split on $IFS, set $1, $2 etc.
    [ "$#" != 4 ] && return 1            # BAD: if not 4 parts
    for PART; do                         # loop over $1, $2 etc.
        case "$PART" in
            *[!0-9]*) return 1           #   BAD: if $PART contains non-digit
        esac
        [ "$PART" -gt 255 ] && return 1  #   BAD: if $PART > 255
    done
    return 0                             # GOOD: nothing bad found
}

With this function you can test your IP address, e.g. to abort your script if the IP address is invalid:

if valid_ip "$IP"; then
    echo "ERROR: IP address '$IP' is invalid" >&2
    exit 4
fi
zrajm
  • 1,361
  • 1
  • 12
  • 21
0

Assuming you are happy with the validation string:

$ s='[0-9]\{1,3\}'
$ echo $ip | grep > /dev/null "^$s\.$s\.$s\.$s$" &&
  echo $ip is valid

Note that this accepts invalid ip addresses like 876.3.4.5

To validate an ip, it's really not convenient to use a regular expression. A relative easy thing to do is:

IFS=. read a b c d << EOF
$ip
EOF

if ( for i in a b c d; do
        eval test \$$i -gt 0 && eval test \$$i -le 255 || exit 1
    done 2> /dev/null )
then
    echo $ip is valid
fi
William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • Great, the only issue is that I need the -E flag to get the regex evaluated (or using egrep) Thanks so much – aseques Feb 14 '12 at 11:30
  • Extended regular expressions should only be needed if you use s='[0-9]{1,3}'. If you escape the brackets, basic regular expressions should work. (Ah, you made your comment prior to my edit...) – William Pursell Feb 14 '12 at 12:26
  • Also, the redirection thing is a bit weird. Just use `egrep -q "regex"` instead. – ghoti Feb 14 '12 at 12:28
  • @ghoti I've always avoided -q for portability reasons, but just discovered that it is specified in the Open Group spec which is probably portable enough! – William Pursell Feb 14 '12 at 12:35
  • @ghoti On the other hand, grep -q could be considered bloat: http://harmful.cat-v.org/cat-v/ – William Pursell Feb 14 '12 at 13:00
  • Whether it's bloat is up for debate. A grep with redirection is guaranteed to traverse every line of input, whereas the `-q` behaviour could be to exit after the first match. Where does one draw the line? Is it better to have a whole 'nuther tool, "gree" for example, to Globally search for a RE, then Exit? – ghoti Feb 15 '12 at 18:34