1

my goal is to store each foliated line (actually each rule) into an array. My output:

Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    ACCEPT     udp  --  109.224.241.0/24     0.0.0.0/0            udp dpt:5060
2    ACCEPT     udp  --  109.224.241.0/24     0.0.0.0/0            udp dpt:4569
3    ACCEPT     udp  --  217.14.138.0/24      0.0.0.0/0            udp dpt:5060
4    ACCEPT     udp  --  217.14.138.0/24      0.0.0.0/0            udp dpt:4569
5    ACCEPT     udp  --  172.30.33.0/24       0.0.0.0/0            udp dpt:5060
6    ACCEPT     udp  --  172.30.33.0/24       0.0.0.0/0            udp dpt:4569
7    ACCEPT     udp  --  212.11.91.0/24       0.0.0.0/0            udp dpt:5060
8    ACCEPT     udp  --  212.11.91.0/24       0.0.0.0/0            udp dpt:4569
9    ACCEPT     udp  --  212.11.64.0/19       0.0.0.0/0            udp dpt:5060
10   ACCEPT     udp  --  212.11.64.0/19       0.0.0.0/0            udp dpt:4569
11   ACCEPT     udp  --  77.240.48.0/20       0.0.0.0/0            udp dpt:5060
12   ACCEPT     udp  --  77.240.48.0/20       0.0.0.0/0            udp dpt:4569
13   LOG        udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:4569 LOG flags 0 level 4 prefix "AsteriskHack:"
14   DROP       udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:4569
15   LOG        udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:5060 LOG flags 0 level 4 prefix "AsteriskHack:"
16   DROP       udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:5060

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination

I do not want to limit rule number. Each chains has several rules.

After I executed my code.:

while IFS='' read line -r || [[ -n "$line" ]]; do
     array+=($line)
done < <(iptables -L --line-numbers)

When I echo ${array[@]} my output is horrible text in one line, so I would like to seperate each line. Actually my content of this array consists of unreal number of iptables rules (after echo ${!array[@]})

I do not know how to have in conditional that foliated rows are compulsory with defined separator (new line). I am not sure if is my separator right.

Thank you for your reply, M

2

1 Answer 1

1

Here's how I'd do it. First, for simplicity, let's list each of the chains individually, since I'm assuming you want to know which chain a rule belongs to:

$ iptables -L INPUT --line-numbers

Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination
1    ACCEPT     udp  --  109.224.241.0/24     0.0.0.0/0            udp dpt:5060
2    ACCEPT     udp  --  109.224.241.0/24     0.0.0.0/0            udp dpt:4569
3    ACCEPT     udp  --  217.14.138.0/24      0.0.0.0/0            udp dpt:5060
4    ACCEPT     udp  --  217.14.138.0/24      0.0.0.0/0            udp dpt:4569
5    ACCEPT     udp  --  172.30.33.0/24       0.0.0.0/0            udp dpt:5060
6    ACCEPT     udp  --  172.30.33.0/24       0.0.0.0/0            udp dpt:4569
7    ACCEPT     udp  --  212.11.91.0/24       0.0.0.0/0            udp dpt:5060
8    ACCEPT     udp  --  212.11.91.0/24       0.0.0.0/0            udp dpt:4569
9    ACCEPT     udp  --  212.11.64.0/19       0.0.0.0/0            udp dpt:5060
10   ACCEPT     udp  --  212.11.64.0/19       0.0.0.0/0            udp dpt:4569
11   ACCEPT     udp  --  77.240.48.0/20       0.0.0.0/0            udp dpt:5060
12   ACCEPT     udp  --  77.240.48.0/20       0.0.0.0/0            udp dpt:4569
13   LOG        udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:4569 LOG flags 0 level 4 prefix "AsteriskHack:"
14   DROP       udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:4569
15   LOG        udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:5060 LOG flags 0 level 4 prefix "AsteriskHack:"
16   DROP       udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:5060

You can get an list of chain names like this:

CHAINS="$(iptables -L | awk '/Chain /{print $2}')"

Now, let's use a few tricks to simply get these into an array:

# We can just define the array from the contents of our command
# output, using \r or \n as a field separator.
# We use grep to ignore lines that don't start with a number.
IFS=$'\r\n' GLOBIGNORE='*' command eval 'INPUT_RULES=($(iptables -L INPUT --line-numbers | grep '^[0-9]'))'

With Bash 4, you can also use the mapfile builtin:

IFS=$'\r\n' mapfile INPUT_RULES < <(iptables -L INPUT --line-numbers | grep '^[0-9]')

Now, I don't know your specific use case, but if you query each chain one at a time, you should also be able to remove the line numbers or use them as keys in an associative array, but maybe they're fine being included.

If you don't want to use grep, but still want to exclude the first two lines from the array, you can just unset the first two elements after the fact, like this:

array=("${array[@]:2}")

Also note that in your original example:

echo "${array[@]}"

will put everything on one line whether it's in separate array keys or not. A better way to accurately view the array with one element per line, would be this:

for rule in "${array[@]}"; do echo LINE: "$rule"; done
2
  • 1
    Could you explain me why is in your code GLOBINGNORE='*' command eval? Thank you.
    – Michal N.
    Commented Feb 3, 2016 at 10:40
  • Setting GLOBIGNORE='*' will prevent wildcard expansion during the eval. There aren't really wildcards in the input we're reading, but, just in case! See here for more info.
    – Will
    Commented Feb 3, 2016 at 13:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .