1

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?

Nikk
  • 7,384
  • 8
  • 44
  • 90
  • 2
    bash doesn't have nested or multidimensional arrays, so neither you nor anybody can do this in any way. There are several _other_ forms you could store this data in bash, or you could use something other than bash like awk, perl, python, but it depends on what you want to do. You could however easily have your awk subsume your two grep's. – dave_thompson_085 May 13 '22 at 09:39
  • @dave_thompson_085 Was referring to something like this: https://stackoverflow.com/a/31468914 – Nikk May 13 '22 at 09:50
  • 1
    @Nikk, that's a single-dimensional array indexed by strings with commas :) – Charles Duffy May 13 '22 at 14:11
  • @Nikk, that said, why don't you just put two words in your values? `existing[0]='286,1.1.1.1'. Easy enough to use parameter expansion to extract only the piece you want. – Charles Duffy May 13 '22 at 14:13
  • @CharlesDuffy Because I want to lookup the array like so `if [[ ! "${existing[*]}" =~ "$a" ]];` – Nikk May 13 '22 at 14:36
  • @Nikk, that's not a reasonable way to do a lookup in the first place; serializing the whole array into a single string and doing a regex on it is crazy inefficient. – Charles Duffy May 13 '22 at 15:04
  • @Nikk, ...that said, if you _want_ to do the crazy, inefficient thing, I don't see why the approach I suggested stops you. – Charles Duffy May 13 '22 at 15:05
  • @Nikk, ...but _really_, if you want to do existence checks, the sane way to do them is to have an associative array indexed by the key whose existence you want to check for; that way it's a constant-time lookup instead of getting slower the larger your data is. – Charles Duffy May 13 '22 at 15:07
  • in the case that you could use multi-dim arrays in bash, what would `${existing[*]}` expand to? `286 1.1.1.1 287 1.1.1.1 ...`? – Fravadona May 13 '22 at 15:34
  • You broke [my answer](https://stackoverflow.com/a/72218586/1745001) when you removed the `,` after `print $1`. Also, you never need grep when you're using awk. It sounds like you're trying to do too much in shell and should be doing more in awk instead. GNU awk does support true multidimensional arrays by the way. – Ed Morton May 14 '22 at 02:03
  • @EdMorton The `awk` command doesn't filter the results and returns nothing. The way I ended up using it is `grep` to filter then `awk` to extract the data based on your answer. – Nikk May 14 '22 at 18:58
  • The awk command I provided does exactly what you asked for given the sample input you provided. You never need grep when you're using awk - piping grep to awk is an anti-pattern. – Ed Morton May 14 '22 at 20:17

1 Answers1

0

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
markp-fuso
  • 28,790
  • 4
  • 16
  • 36