shell - 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
解决方案
本机 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看到上面运行的代码
推荐阅读
- ruby - 请帮我解释如何在不使用 Array#sort Ruby 的情况下对数组进行排序
- python-3.x - 将数据框从宽转换为长,同时拆分列名
- laravel - Laravel 与 Vue 路由问题,没有切换到正确的布局
- reactjs - 将 React 和 CSS 模块捆绑到 JavaScript 中的 Snowpack
- linux - 不使用库时的动态链接?
- google-apps-script - 检查 URL 是否属于同一文档域中的资源
- linux - Apropos/Man -k 只显示几个手册页
- kubernetes - kubectl 适用于服务器但不适用于容器
- javascript - React 中的选项卡式内容
- angular - Angular 11 三元表达式 *ngIF