In your regex a blank string is allowed, as your pattern allows a string to not match any of the optional parts so its passing empty string.
Let me put my thoughts here -
My choice would be Python with using inet_pton
#!/usr/bin/env python
import socket
import sys
try:
socket.inet_pton(socket.AF_INET6, sys.argv[1])
result=0
except socket.error:
result=1
sys.exit(result)
If you want to use shell script then try the following found on net not tested. (src: http://twobit.us/2011/07/validating-ip-addresses/ )
#!/bin/sh
WORD="[0-9A-Fa-f]\{1,4\}"
# flat address, no compressed words
FLAT="^${WORD}\(:${WORD}\)\{7\}$"
# ::'s compressions excluding beginning and end edge cases
COMP2="^\(${WORD}:\)\{1,1\}\(:${WORD}\)\{1,6\}$"
COMP3="^\(${WORD}:\)\{1,2\}\(:${WORD}\)\{1,5\}$"
COMP4="^\(${WORD}:\)\{1,3\}\(:${WORD}\)\{1,4\}$"
COMP5="^\(${WORD}:\)\{1,4\}\(:${WORD}\)\{1,3\}$"
COMP6="^\(${WORD}:\)\{1,5\}\(:${WORD}\)\{1,2\}$"
COMP7="^\(${WORD}:\)\{1,6\}\(:${WORD}\)\{1,1\}$"
# trailing :: edge case, includes case of only :: (all 0's)
EDGE_TAIL="^\(\(${WORD}:\)\{1,7\}\|:\):$"
# leading :: edge case
EDGE_LEAD="^:\(:${WORD}\)\{1,7\}$"
is_ipv6 () {
echo $1 | grep --silent "\(${FLAT}\)\|\(${COMP2}\)\|\(${COMP3}\)\|\(${COMP4}\)\|\(${COMP5}\)\|\(${COMP6}\)\|\(${COMP7}\)\|\(${EDGE_TAIL}\)\|\(${EDGE_LEAD}\)"
if [ $? -eq 0 ]; then
return 1
fi
return 0
}
is_ipv6 $1
if [ $? -eq 1 ]; then
exit 0
else
echo "Invalid IPv6 address: $1" >&2
exit 1
fi