When deploying a gbase database cluster, you often need to handle IPv6 addresses in configuration files. Many tools require the full 8‑segment, 4‑digit hexadecimal format, while real‑world addresses often come in compressed notation (e.g., 2001:db8::1). This article presents a pure Bash script that expands any compressed IPv6 address to the complete, canonical form, with built‑in validation and prefix handling.
Features
- Expands any compressed IPv6 address (including
::zero‑compression) into 8 segments of 4 lowercase hex digits - Handles addresses with prefixes (e.g.,
2001:db8::1/64), with an option to preserve or strip the prefix - Built‑in validation: illegal characters,
::occurrence count, segment length, and hex validity - Pure Shell implementation; relies only on standard tools (
bc,printf)
The Script
#!/bin/bash
set -euo pipefail
SCRIPT_NAME=$(basename "$0")
show_help() {
cat << EOF
Usage: $SCRIPT_NAME [options] <IPv6 address>
Expands a compressed IPv6 address to the full 8‑segment, 4‑digit format.
Supports address validation and prefix handling.
Options:
-h, --help Show this help message and exit
-p, --prefix Preserve the prefix (e.g., 2001:db8::1/64); by default only the address part is expanded
Examples:
$SCRIPT_NAME 2001:db8::1
$SCRIPT_NAME -p ::1/128
$SCRIPT_NAME fe80::1234:5678:9abc:def0
EOF
}
validate_ipv6_chars() {
local addr="$1"
if [[ ! "$addr" =~ ^[0-9a-fA-F:/]+$ ]]; then
echo "Error: IPv6 address contains illegal characters (only 0-9/a-f/A-F/:/ allowed)" >&2
exit 1
fi
}
expand_ipv6_core() {
local pure_addr="$1"
local expanded_segments=()
local colon_count=$(grep -o "::" <<< "$pure_addr" | wc -l)
if [[ $colon_count -gt 1 ]]; then
echo "Error: Invalid IPv6 address (:: can appear only once)" >&2
exit 1
fi
if [[ "$pure_addr" == "::" ]]; then
echo "0000:0000:0000:0000:0000:0000:0000:0000"
return
fi
local temp_addr="${pure_addr//::/:__ZERO__:}"
local segments=()
IFS=: read -ra segments <<< "$temp_addr"
local filtered_segments=()
for seg in "${segments[@]}"; do
if [[ -n "$seg" ]]; then
filtered_segments+=("$seg")
fi
done
segments=("${filtered_segments[@]}")
local zero_pos=-1
local seg_count=${#segments[@]}
for i in "${!segments[@]}"; do
if [[ "${segments[$i]}" == "__ZERO__" ]]; then
zero_pos=$i
break
fi
done
local new_segments=()
if [[ $zero_pos -ne -1 ]]; then
local fill_zeros=$((8 - (seg_count - 1)))
if [[ $fill_zeros -lt 0 ]]; then
echo "Error: IPv6 address has too many segments (more than 8)" >&2
exit 1
fi
for ((i=0; i<zero_pos; i++)); do
new_segments+=("${segments[$i]}")
done
for ((i=0; i<fill_zeros; i++)); do
new_segments+=("")
done
for ((i=zero_pos+1; i<seg_count; i++)); do
new_segments+=("${segments[$i]}")
done
else
new_segments=("${segments[@]}")
if [[ ${#new_segments[@]} -ne 8 ]]; then
echo "Error: IPv6 address segment count wrong (must be exactly 8 when no ::)" >&2
exit 1
fi
fi
for seg in "${new_segments[@]}"; do
if [[ -z "$seg" || "$seg" == "0" ]]; then
expanded_segments+=("0000")
continue
fi
if [[ ${#seg} -gt 4 ]]; then
echo "Error: IPv6 segment '$seg' too long (max 4 digits)" >&2
exit 1
fi
if ! [[ "$seg" =~ ^[0-9a-fA-F]{1,4}$ ]]; then
echo "Error: IPv6 segment '$seg' contains illegal characters" >&2
exit 1
fi
local seg_upper=$(echo "$seg" | tr 'a-f' 'A-F')
local seg_10=$(echo "ibase=16; $seg_upper" | bc)
local seg_4digit=$(printf "%04x" "$seg_10" | tr 'A-F' 'a-f')
expanded_segments+=("$seg_4digit")
done
local expanded_addr=$(IFS=:; echo "${expanded_segments[*]}")
echo "$expanded_addr"
}
expand_ipv6() {
local addr="$1"
local keep_prefix="$2"
local pure_addr
local prefix=""
if [[ "$addr" =~ / ]]; then
pure_addr="${addr%/*}"
prefix="${addr#*/}"
if ! [[ "$prefix" =~ ^[0-9]+$ ]] || [[ "$prefix" -lt 0 ]] || [[ "$prefix" -gt 128 ]]; then
echo "Error: Invalid IPv6 prefix '$prefix' (must be 0‑128)" >&2
exit 1
fi
else
pure_addr="$addr"
fi
validate_ipv6_chars "$pure_addr"
local expanded_addr=$(expand_ipv6_core "$pure_addr")
if [[ "$keep_prefix" -eq 1 && -n "$prefix" ]]; then
echo "${expanded_addr}/${prefix}"
else
echo "$expanded_addr"
fi
}
KEEP_PREFIX=0
IPV6_ADDR=""
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
show_help
exit 0
;;
-p|--prefix)
KEEP_PREFIX=1
shift
;;
*)
if [[ -z "$IPV6_ADDR" ]]; then
IPV6_ADDR="$1"
shift
else
echo "Error: Extra argument '$1'" >&2
show_help >&2
exit 1
fi
;;
esac
done
if [[ -z "$IPV6_ADDR" ]]; then
echo "Error: IPv6 address is required" >&2
show_help >&2
exit 1
fi
expand_ipv6 "$IPV6_ADDR" "$KEEP_PREFIX"
Running the Script
$ ./ipv6_expand.sh -p 2001:1::5:0:0:8/64
2001:0001:0000:0000:0005:0000:0000:0008/64
$ ./ipv6_expand.sh 2001:1::5:0:0:8
2001:0001:0000:0000:0005:0000:0000:0008
When configuring inter‑node communication in a gbase database cluster that involves IPv6, this script helps you normalize addresses — ensuring they are consistently written in configuration files and simplifying address comparisons during automated deployments.
Top comments (0)