Pure Shell Script to Compress IPv6 Addresses

When configuring a gbase database cluster, you often need to convert IPv6 addresses into the standard RFC compressed format — turning 2001:db8:0:0:0:0:0:1 into 2001:db8::1, for example. This article provides a pure Bash script that performs IPv6 addres…


This content originally appeared on DEV Community and was authored by Michael

When configuring a gbase database cluster, you often need to convert IPv6 addresses into the standard RFC compressed format — turning 2001:db8:0:0:0:0:0:1 into 2001:db8::1, for example. This article provides a pure Bash script that performs IPv6 address compression without any external tools like iproute2. It validates the address, finds the longest run of zero segments, and outputs a fully lower‑cased compressed form, optionally preserving a prefix.

Features

  • Compresses any valid IPv6 address to the canonical RFC format by replacing the longest consecutive zero blocks with ::
  • Handles addresses with prefixes (e.g., /64); can keep or drop the prefix
  • Built‑in checks for illegal characters, segment length, and :: occurrence count
  • Pure Shell implementation requiring only 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>

Compresses an IPv6 address to the standard RFC format.
Supports address validation, zero‑block compression, 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 compressed

Examples:
    $SCRIPT_NAME 2001:2:3:4:5:6:7:8
    $SCRIPT_NAME -p 2001:db8:0:0:0:0:0:1/64
    $SCRIPT_NAME ::1
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() {
    local pure_addr="$1"
    local expanded=""

    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
        expanded="0:0:0:0:0:0:0:0"
        echo "$expanded"
        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+=("0")
        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 [[ ${#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
    done

    expanded=$(IFS=:; echo "${new_segments[*]}")
    echo "$expanded"
}

compress_ipv6() {
    local expanded_addr="$1"
    local segments=()
    IFS=: read -ra segments <<< "$expanded_addr"
    local compressed_segments=()

    for seg in "${segments[@]}"; do
        if [[ -z "$seg" || "$seg" == "0000" || "$seg" == "000" || "$seg" == "00" ]]; then
            compressed_segments+=("0")
        else
            local seg_upper=$(echo "$seg" | tr 'a-f' 'A-F')
            local trimmed=$(echo "obase=16; ibase=16; $seg_upper" | bc | tr 'A-F' 'a-f')
            compressed_segments+=("$trimmed")
        fi
    done

    local max_zero_len=0
    local max_zero_start=-1
    local current_zero_len=0
    local current_zero_start=-1

    for i in "${!compressed_segments[@]}"; do
        if [[ "${compressed_segments[$i]}" == "0" ]]; then
            if [[ $current_zero_start -eq -1 ]]; then
                current_zero_start=$i
            fi
            ((current_zero_len++))
            if [[ $current_zero_len -gt $max_zero_len ]]; then
                max_zero_len=$current_zero_len
                max_zero_start=$current_zero_start
            fi
        else
            current_zero_len=0
            current_zero_start=-1
        fi
    done

    local final_segments=()
    local skip=0
    for i in "${!compressed_segments[@]}"; do
        if [[ $skip -gt 0 ]]; then
            ((skip--))
            continue
        fi
        if [[ $i -eq $max_zero_start && $max_zero_len -ge 2 ]]; then
            final_segments+=("")
            skip=$((max_zero_len - 1))
            if [[ $((i + max_zero_len)) -eq 8 ]]; then
                final_segments+=("")
            fi
        else
            final_segments+=("${compressed_segments[$i]}")
        fi
    done

    local compressed=$(IFS=:; echo "${final_segments[*]}")
    compressed="${compressed//:::/::}"
    if [[ "$compressed" == ":"* ]]; then
        compressed=":$compressed"
    fi
    if [[ "$compressed" == "::0" ]]; then
        compressed="::"
    fi

    compressed=$(echo "$compressed" | tr 'A-F' 'a-f')
    echo "$compressed"
}

format_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 "$pure_addr")

    local formatted_addr=$(compress_ipv6 "$expanded_addr")

    if [[ "$keep_prefix" -eq 1 && -n "$prefix" ]]; then
        echo "${formatted_addr}/${prefix}"
    else
        echo "$formatted_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

format_ipv6 "$IPV6_ADDR" "$KEEP_PREFIX"

Running the Script

$ ./ipv6_format.sh 2001:1:0:0:5:0:0:8
2001:1::5:0:0:8
$ ./ipv6_format.sh -p 2001:1:0:0:5:0:0:8/64
2001:1::5:0:0:8/64

In a gbase database cluster, this script helps you standardize IPv6 addresses into their shortest canonical form — making configuration files cleaner and simplifying automated deployment and address comparison tasks.


This content originally appeared on DEV Community and was authored by Michael


Print Share Comment Cite Upload Translate Updates
APA

Michael | Sciencx (2026-07-07T14:15:00+00:00) Pure Shell Script to Compress IPv6 Addresses. Retrieved from https://www.scien.cx/2026/07/07/pure-shell-script-to-compress-ipv6-addresses/

MLA
" » Pure Shell Script to Compress IPv6 Addresses." Michael | Sciencx - Tuesday July 7, 2026, https://www.scien.cx/2026/07/07/pure-shell-script-to-compress-ipv6-addresses/
HARVARD
Michael | Sciencx Tuesday July 7, 2026 » Pure Shell Script to Compress IPv6 Addresses., viewed ,<https://www.scien.cx/2026/07/07/pure-shell-script-to-compress-ipv6-addresses/>
VANCOUVER
Michael | Sciencx - » Pure Shell Script to Compress IPv6 Addresses. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2026/07/07/pure-shell-script-to-compress-ipv6-addresses/
CHICAGO
" » Pure Shell Script to Compress IPv6 Addresses." Michael | Sciencx - Accessed . https://www.scien.cx/2026/07/07/pure-shell-script-to-compress-ipv6-addresses/
IEEE
" » Pure Shell Script to Compress IPv6 Addresses." Michael | Sciencx [Online]. Available: https://www.scien.cx/2026/07/07/pure-shell-script-to-compress-ipv6-addresses/. [Accessed: ]
rf:citation
» Pure Shell Script to Compress IPv6 Addresses | Michael | Sciencx | https://www.scien.cx/2026/07/07/pure-shell-script-to-compress-ipv6-addresses/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.