【问题标题】:expand ipv6 address in shell script在 shell 脚本中扩展 ipv6 地址
【发布时间】:2013-02-04 23:04:33
【问题描述】:

我想根据给定的 IPv6 地址更新 djbdns (dbndns) 配置文件,例如2a01:488:66:1000:523:f116:0:1::1

dbndns 需要扩展的 IPv6 地址,例如2a010488006610000523f116000000012a01:488:66:1000:523:f116:0:1

扩展这种 IPv6 地址最简单的方法是什么?

【问题讨论】:

    标签: shell ipv6


    【解决方案1】:

    使用sipcalc 可能会做到这一点。它提供了比您需要的更多的信息,但 grepcut 可以解决这个问题:-)

    $ EXPANDED=`sipcalc 2001::1 | fgrep Expanded | cut -d '-' -f 2`
    $ echo $EXPAND
    2001:0000:0000:0000:0000:0000:0000:0001
    

    供参考,这是sipcalc的完整输出:

    $ sipcalc 2001::1
    -[ipv6 : 2001::1] - 0
    
    [IPV6 INFO]
    Expanded Address        - 2001:0000:0000:0000:0000:0000:0000:0001
    Compressed address      - 2001::1
    Subnet prefix (masked)  - 2001:0:0:0:0:0:0:1/128
    Address ID (masked)     - 0:0:0:0:0:0:0:0/128
    Prefix address          - ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
    Prefix length           - 128
    Address type            - Aggregatable Global Unicast Addresses
    Network range           - 2001:0000:0000:0000:0000:0000:0000:0001 -
                              2001:0000:0000:0000:0000:0000:0000:0001
    

    【讨论】:

      【解决方案2】:

      我最近想要一个可以跨 shell 移植并在诸如 openwrt 等平台上工作的无依赖解决方案。我想出了以下sn-p:

      # helper to convert hex to dec (portable version)
      hex2dec(){
          [ "$1" != "" ] && printf "%d" "$(( 0x$1 ))"
      }
      
      # expand an ipv6 address
      expand_ipv6() {
          ip=$1
      
          # prepend 0 if we start with :
          echo $ip | grep -qs "^:" && ip="0${ip}"
      
          # expand ::
          if echo $ip | grep -qs "::"; then
              colons=$(echo $ip | sed 's/[^:]//g')
              missing=$(echo ":::::::::" | sed "s/$colons//")
              expanded=$(echo $missing | sed 's/:/:0/g')
              ip=$(echo $ip | sed "s/::/$expanded/")
          fi
      
          blocks=$(echo $ip | grep -o "[0-9a-f]\+")
          set $blocks
      
          printf "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n" \
              $(hex2dec $1) \
              $(hex2dec $2) \
              $(hex2dec $3) \
              $(hex2dec $4) \
              $(hex2dec $5) \
              $(hex2dec $6) \
              $(hex2dec $7) \
              $(hex2dec $8)
      }
      

      我也有这个功能可以压缩

      # returns a compressed ipv6 address under the form recommended by RFC5952
      compress_ipv6() {
          ip=$1
      
          blocks=$(echo $ip | grep -o "[0-9a-f]\+")
          set $blocks
      
          # compress leading zeros
          ip=$(printf "%x:%x:%x:%x:%x:%x:%x:%x\n" \
              $(hex2dec $1) \
              $(hex2dec $2) \
              $(hex2dec $3) \
              $(hex2dec $4) \
              $(hex2dec $5) \
              $(hex2dec $6) \
              $(hex2dec $7) \
              $(hex2dec $8)
          )
      
          # prepend : for easier matching
          ip=:$ip
      
          # :: must compress the longest chain
          for pattern in :0:0:0:0:0:0:0:0 \
                  :0:0:0:0:0:0:0 \
                  :0:0:0:0:0:0 \
                  :0:0:0:0:0 \
                  :0:0:0:0 \
                  :0:0; do
              if echo $ip | grep -qs $pattern; then
                  ip=$(echo $ip | sed "s/$pattern/::/")
                  # if the substitution occured before the end, we have :::
                  ip=$(echo $ip | sed 's/:::/::/')
                  break # only one substitution
              fi
          done
      
          # remove prepending : if necessary
          echo $ip | grep -qs "^:[^:]" && ip=$(echo $ip | sed 's/://')
      
          echo $ip
      }
      

      您可以将它们结合起来测试给定的输入是否为 ipv6

      # a valid ipv6 is either the expanded form or the compressed one
      is_ipv6(){
          expanded="$(expand_ipv6 $1)"
          [ "$1" = "$expanded" ] && return 0
          compressed="$(compress_ipv6 $expanded)"
          [ "$1" = "$compressed" ] && return 0
          return 1
      }
      

      我希望这会有所帮助!这些 sn-ps 取自 https://github.com/chmduquesne/wg-ip。如果您发现任何错误,请贡献!

      【讨论】:

      • 你有一个错误,就在这一行之后:` :0:0:0:0 \ ` 你缺少如下一行:` :0:0:0 \ `
      【解决方案3】:

      这适合你吗?

      kent$  echo "2a01:488:66:1000:523:f116:0:1"|awk -F: '{for(i=1;i<=NF;i++)x=x""sprintf ("%4s", $i);gsub(/ /,"0",x);print x}'
      2a010488006610000523f11600000001
      

      【讨论】:

      • @chepner 我不熟悉 ipv6,抱歉。您能否将其粘贴为示例输入并在您的问题中给出预期的输出?
      • 在 IPv6 中,您可以将一连串的 0 压缩成一个空字段。例如,2a01:0:0:0:0:0:0:1 可以写成2a01::1。对于任何类型的自动化处理来说,这都是一场噩梦。
      • @chepner 我想我明白了。可以通过awk完成,你必须检查NF,如果它0
      【解决方案4】:
      __rfc5952_expand () {
          read addr mask < <(IFS=/; echo $1)
          quads=$(grep -oE "[a-fA-F0-9]{1,4}" <<< ${addr/\/*} | wc -l)
          #[ "${addr:${#addr}-1}" == ":" ] && { addr="${addr}0000"; (( quads++ )); }
          grep -qs ":$" <<< $addr && { addr="${addr}0000"; (( quads++ )); }
          grep -qs "^:" <<< $addr && { addr="0000${addr}"; (( quads++ )); }
          [ $quads -lt 8 ] && addr=${addr/::/:$(for (( i=1; i<=$(( 8 - quads )) ; i++ )); do printf "0000:"; done)}
          #addr=$(
          #for quad in $(IFS=:; echo ${addr}); do
          #    [ "${#quad}" -lt 4 ] && for (( i=${#quad}; i<4 ; i++ )); do quad=0${quad}; done
          #    printf "${delim}${quad}"; delim=":";
          # Or so if you need result without colon, as asked in first post
          #   printf "${quad}";
          #done)
          addr=$(for quad in $(IFS=:; echo ${addr}); do printf "${delim}%04x" "0x${quad}"; delim=":"; done)
          #addr=$(for quad in $(IFS=:; echo ${addr}); do printf "%04x" "0x${quad}"; done)  
          [ ! -z $mask ] && echo $addr/$mask || echo $addr
      }
      
      for ip in 2a01:4f8:211:9e::/64 ::1/128; do __rfc5952_expand $ip; done
      
      2a01:04f8:0211:009e:0000:0000:0000:0000/64
      0000:0000:0000:0000:0000:0000:0000:0001/128
      
      __rfc5952_compact () {
          read addr mask < <(IFS=/; echo $1)
          addr=$(for quad in $(IFS=:; echo ${addr}); do printf "${delim}%x" "0x${quad}"; delim=":"; done)
          for zeros in $(grep -oE "((^|:)0)+:?" <<< $addr | sort -r | head -1); do addr=${addr/$zeros/::}; done
          [ ! -z $mask ] && echo $addr/$mask || echo $addr
      }
      
      for ip in 2a01:04f8:0211:009e:00:0001:0000:0000/64 0000:0000:0000:0000:0000:0000:0000:0001/128; do __rfc5952_compact $ip; done
      
      2a01:4f8:211:9e:0:1::/64
      ::1/128
      

      【讨论】:

      • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
      猜你喜欢
      • 2011-06-15
      • 2017-04-11
      • 1970-01-01
      • 2019-03-31
      • 1970-01-01
      • 2012-08-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多