首页 > 解决方案 > Parse multiple values of IP addresses in shell script

问题描述

I have a script which reads through IPSec .conf files and looks for the string "conn", saves the name of the connection, and then takes an IP address from a line containing "rightsource". Currently there is a one-to-one relationship between the two - if there is one line saying "conn example1" there will be just one line saying rightsource with a single IP address. The part of the script which currently deals with this case is:

for CONF in *.conf; do
  set +e
  declare -a CONNS=($(awk '/^conn/ {print $2}' < $CONF | fgrep -vi common))
  declare -a RSOURCES=($(awk -F\= '/(^ *rightsource|^ *# *effectiverightsource)/ {print $2}' < $CONF))

  CONN_SIZE=${#CONNS[@]}
  SOURCE_SIZE=${#RSOURCES[@]}
  if [ $CONN_SIZE != $SOURCE_SIZE ]; then
    #echo "Problem: number of connections $CONN_SIZE not equal to number of source IPs $SOURCE_SIZE in $CONF."
    #echo "Connections=${CONNS[@]}"
    #echo "Source IPs=${RSOURCES[@]}"
    continue
  fi

My problem is that I would like to switch our IPSec conf files to a better syntax. Rather than a one to one relationship between conn and rightsource like this:

 conn example79
        rightsourceip=44.45.46.79
 conn example80
        rightsourceip=44.45.46.80

I would like to use a more efficient syntax:

conn example
        rightsubnets={44.45.46.79/32,44.45.46.80/32}

The "rightsubnets" curly braces could contain 1, 2 or more IP addresses, which is what I need to pass to the rest of the script so that it can try to ping each one to make sure it's still available.

I can just about understand what the awk command in my existing script is doing, but I have no idea how best to also look for the variable amounts of IP addresses which might be found in the newer syntax. Any suggestions would be most appreciated!

Thanks, Bob

标签: shell

解决方案


本机 bash 中的实现(在调试工具上花费了相当多的时间)可能如下所示:

#!/usr/bin/env bash
case $BASH_VERSION in [123].*) echo "ERROR: Requires bash 4.0 or newer" >&2; exit 1;; esac
PS4=':$LINENO+' # if trace logging is enabled, include line numbers

conn_re='^[[:space:]]*conn[[:space:]]+([^[:space:]].*)$'
rightsource_re='^[[:space:]]*(effective)?right(source|subnet)s?=(.*)$'
multimatch_re='^[{](.*,.*)[}]$'

# read document from stdin, and populate an associative array     
declare -A subnets=( )

for conf in *.conf; do : conf="$conf"
  conn=''
  while IFS= read -r line; do : line="$line"
    [[ $line =~ $conn_re ]] && { conn=${BASH_REMATCH[1]}; continue; }
    [[ $line =~ $rightsource_re ]] && { subnets[$conn]=${BASH_REMATCH[3]}; }
  done <"$conf"
done
[[ $- = *x* ]] && declare -p subnets >&2 # if tracing enabled, log our extracted values.

handle_result() {
  local conn=$1 element=$2
  element=${element%/32}
  echo "For conn $conn, need to handle element $element"
}

# iterate over the associative array, and call the handler for each address
for conn in "${!subnets[@]}"; do : conn="$conn"
  rightsource=${subnets[$conn]}
  if [[ $rightsource =~ $multimatch_re ]]; then
    IFS=, read -r -a elements <<<"${BASH_REMATCH[1]}"
    for element in "${elements[@]}"; do
      handle_result "$conn" "$element"
    done
  else
    handle_result "$conn" "$rightsource"
  fi
done

如果给出输入:

conn example
        rightsubnets={44.45.46.79/32,44.45.46.80/32}
conn next_example
        rightsubnets=1.2.3.4
conn third_example
        rightsubnets={5.6.7.8,9.10.11.12/32}

...这会发出输出:

For conn example, need to handle element 44.45.46.79
For conn example, need to handle element 44.45.46.80
For conn third_example, need to handle element 5.6.7.8
For conn third_example, need to handle element 9.10.11.12
For conn next_example, need to handle element 1.2.3.4

...但当然,您可以更改handle_result功能以执行您需要的任何 ping 或其他测试。


您可以在https://ideone.com/QrE1Ff看到上面运行的代码


推荐阅读