Home > front end >  AWK return/print as associative arrays in Bash
AWK return/print as associative arrays in Bash

Time:05-14

With this command I am trying to filter through my firewall. In order to extract the firewall rule ID number and the IP address.

existing=($(ufw status numbered | grep -e ''"$ssh_port"'' | grep -i 'SSH' | awk '{gsub(/[][]/,""); print $1 $5}'))

The raw output from ufw status numbered looks like this:

[286] 22                        ALLOW IN    1.1.1.1                    # SSH
[287] 22                        ALLOW IN    1.1.1.1                    # SSH
[299] 22                        ALLOW IN    aaaa:aaaa:aaaa:aaaa::aaaa  # SSH

What I am tying to do is return print $1 $5 as an array.

In order to access each line in bash like this:

echo ${existing[0][0]} -> 286
echo ${existing[0][1]} -> 1.1.1.1

echo ${existing[1][0]} -> 287
echo ${existing[1][1]} -> 1.1.1.1

echo ${existing[2][0]} -> 299
echo ${existing[2][1]} -> aaaa:aaaa:aaaa:aaaa::aaaa

How can I achieve this?

CodePudding user response:

Simulating the ufx output:

$ cat ufw.dat
[286] 22                        ALLOW IN    1.1.1.1                    # SSH
[287] 22                        ALLOW IN    1.1.1.1                    # SSH
[299] 22                        ALLOW IN    aaaa:aaaa:aaaa:aaaa::aaaa  # SSH

One idea where we let awk pass the desired data to a bash/while loop for parsing and storage in a pair of arrays:

declare -a id_array=()
declare -a ip_array=()

ndx=-1

while read -r id ip
do
        (( ndx   ))
        id_array[$ndx]="${id}"
        ip_array[$ndx]="${ip}"

done < <(cat ufw.dat | awk '/SSH/ {gsub(/[][]/,""); print $1,$5}')

This generates:

$ declare -p id_array ip_array
declare -a id_array=([0]="286" [1]="287" [2]="299")
declare -a ip_array=([0]="1.1.1.1" [1]="1.1.1.1" [2]="aaaa:aaaa:aaaa:aaaa::aaaa")

OP can now use the common index to access both components at the same time, eg,

for i in "${!id_array[@]}"
do
    echo "$i : ${id_array[$i]} : ${ip_array[$i]}"
done

0 : 286 : 1.1.1.1
1 : 287 : 1.1.1.1
2 : 299 : aaaa:aaaa:aaaa:aaaa::aaaa

CodePudding user response:

Using a sparse bash array (which is equivalent to an associative array in your case) might be enough for you:

existing=()
while read id ip
do
    existing[id]=$ip
done < <(
    ufw status numbered |
    awk -v port="$ssh_port" '
        $2 == port {
            gsub(/[[\]]/,"",$1)
            print $1, $5
        }'
)
  • if you need to loop through the existing indexes/values:
for i in "${!existing[@]}"
do
    echo "$i -> ${existing[i]}"
done
  • if you need to check if an index is defined:
[[ ${existing[i]-1} ]] && echo "index $i exists"
  • if you need all the indexes in a space separated string:
echo "${!existing[*]}"
  • if you need all the ip addresses in a space separated string:
echo "${existing[*]}"
  • Related