解析shell脚本中IP地址的多个值

时间:2018-06-22 16:03:02

标签: shell

我有一个脚本,该脚本读取IPSec .conf文件并查找字符串“ conn”,保存连接的名称,然后从包含“ rightsource”的行中获取IP地址。当前,两者之间存在一对一的关系-如果有一行说“ conn example1”,那么只有一行说正确的来源是一个IP地址。当前处理这种情况的脚本部分是:

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

我的问题是我想将IPSec conf文件切换为更好的语法。而不是像这样在conn和rightsource之间建立一对一的关系:

 conn example79
        rightsourceip=44.45.46.79
 conn example80
        rightsourceip=44.45.46.80

我想使用一种更有效的语法:

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

“ rightsubnets”花括号可以包含1个,2个或更多IP地址,这是我需要传递给脚本其余部分的地址,以便它可以尝试对每个脚本进行ping操作以确保其仍然可用。

我几乎可以了解现有脚本中的awk命令在做什么,但是我不知道如何最好地查找在较新语法中可以找到的可变数量的IP地址。任何建议将不胜感激!

谢谢, 鲍勃

2 个答案:

答案 0 :(得分:0)

我的建议是将rightsubnets添加到您的RSOURCES行中,然后稍后在脚本中处理一行或更多行。也就是说,如果特定的RSOURCE具有花括号,则将其作为一个或多个子网处理,否则将与今天一样。这是一个最小的更改,扩展了现有代码,可以满足您的新(附加)要求。

我的另一个建议是,您可能已经超出了shell的舒适范围,也许您应该考虑使用python或perl或类似的东西,在那里您可以构建更好的状态机并更可靠地处理每一行。例如,如果某人忘记在给定的连接中放入rightsourceip,但将其与另一个条目放在一起(这样它就有两个),那可能是一个错误,但是您将无法轻松地告诉壳。 (IPSec可能会遇到这样的问题,可能不够,也可能不够。根据我的经验,像您这样的脚本经常在重新启动依赖于这些conf文件的工具之前运行,因此额外的错误检测很有帮助。)这不是唯一的问题。可以长成一个更健壮的语言更容易掌握的语言,当然还有很多其他语言。它也可以更快,因为它将只浏览每个文件一次,而您的shell脚本则要读取每个文件至少两次。

答案 1 :(得分:0)

本机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上看到上面的代码