Arrays are necessary for almost all programming problems, but until today I have not really learnt how to use them in BASH. The particular need this time was to clean up port forwarding entries in the UPnP table of the router, for which I use upnpc.
To delete an UPnP entry, the man page of upnpc gives the following syntax:
Delete port redirection: upnpc [options] -d external_port protocol [port2 protocol2] [...]
And to get a list of the current entries, give the "-l"
option
upnpc -l upnpc : miniupnpc library test client. (c) 2006-2010 Thomas Bernard Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ for more information. List of UPNP devices found on the network : desc: http://192.168.1.1:56688/picsdesc.xml st: urn:schemas-upnp-org:device:InternetGatewayDevice:1 Found valid IGD : http://192.168.1.1:56688/upnp/control/WANIPConnection Local LAN ip address : 192.168.1.4 Connection Type : IP_Routed Status : Connected, uptime=252838s, LastConnectionError : ERROR_NONE Time started : Mon Dec 23 13:30:13 2013 MaxBitRateDown : 100000000 bps MaxBitRateUp 100000000 bps ExternalIPAddress = 217.210.110.223 0 TCP 19827->192.168.1.4:22 'libminiupnpc' '' 1 TCP 25550->192.168.1.8:25550 'Spotify' '' 2 UDP 26954->192.168.1.8:26954 'Skype UDP at 192.168.1.8:26954 (2551)' '' 3 UDP 26955->192.168.1.8:26954 'Skype UDP at 192.168.1.8:26954 (2550)' '' 4 TCP 43901->192.168.1.4:43901 'Spotify' '' 5 TCP 22->192.168.1.2:22 'libminiupnpc' '' 6 TCP 80->192.168.1.2:80 'libminiupnpc' '' 7 TCP 143->192.168.1.2:143 'libminiupnpc' '' 8 TCP 993->192.168.1.2:993 'libminiupnpc' '' 9 UDP 15453->192.168.1.3:15453 'Skype UDP at 192.168.1.3:15453 (2550)' '' 10 TCP 15453->192.168.1.3:15453 'Skype TCP at 192.168.1.3:15453 (2550)' '' 11 TCP 26954->192.168.1.8:26954 'Skype TCP at 192.168.1.8:26954 (2551)' '' 12 TCP 51909->192.168.1.4:51909 'Deluge 1.3.3 at 192.168.1.4:51909' '' 13 UDP 51909->192.168.1.4:51909 'Deluge 1.3.3 at 192.168.1.4:51909' '' 14 TCP 55984->192.168.1.4:55984 'Deluge 1.3.3 at 192.168.1.4:55984' '' 15 UDP 55984->192.168.1.4:55984 'Deluge 1.3.3 at 192.168.1.4:55984' '' 16 TCP 63344->192.168.1.4:63344 'Deluge 1.3.3 at 192.168.1.4:63344' '' 17 UDP 63344->192.168.1.4:63344 'Deluge 1.3.3 at 192.168.1.4:63344' '' 18 TCP 61757->192.168.1.4:61757 'Deluge 1.3.3 at 192.168.1.4:61757' '' 19 UDP 61757->192.168.1.4:61757 'Deluge 1.3.3 at 192.168.1.4:61757' '' 20 TCP 62083->192.168.1.4:62083 'Deluge 1.3.3 at 192.168.1.4:62083' '' 21 UDP 62083->192.168.1.4:62083 'Deluge 1.3.3 at 192.168.1.4:62083' '' 22 TCP 51470->192.168.1.4:51470 'Deluge 1.3.3 at 192.168.1.4:51470' '' 23 UDP 51470->192.168.1.4:51470 'Deluge 1.3.3 at 192.168.1.4:51470' '' 24 TCP 53722->192.168.1.4:53722 'Deluge 1.3.3 at 192.168.1.4:53722' '' 25 UDP 53722->192.168.1.4:53722 'Deluge 1.3.3 at 192.168.1.4:53722' '' 26 TCP 62601->192.168.1.4:62601 'Deluge 1.3.3 at 192.168.1.4:62601' '' 27 UDP 62601->192.168.1.4:62601 'Deluge 1.3.3 at 192.168.1.4:62601' '' 28 TCP 64600->192.168.1.4:64600 'Deluge 1.3.3 at 192.168.1.4:64600' '' 29 UDP 64600->192.168.1.4:64600 'Deluge 1.3.3 at 192.168.1.4:64600' '' 30 TCP 64362->192.168.1.4:64362 'Deluge 1.3.3 at 192.168.1.4:64362' '' 31 UDP 64362->192.168.1.4:64362 'Deluge 1.3.3 at 192.168.1.4:64362' '' 32 TCP 58703->192.168.1.4:58703 'Deluge 1.3.3 at 192.168.1.4:58703' '' 33 UDP 58703->192.168.1.4:58703 'Deluge 1.3.3 at 192.168.1.4:58703' '' GetGenericPortMappingEntry() returned 713 (SpecifiedArrayIndexInvalid)
The port forwarding entries made by Deluge were obsolete, and I wanted to remove them with a BASH script. Filtering out the Deluge entries was easily done with grep
, which gives us the the following table:
upnpc -l | grep Deluge 12 TCP 51909->192.168.1.4:51909 'Deluge 1.3.3 at 192.168.1.4:51909' '' 13 UDP 51909->192.168.1.4:51909 'Deluge 1.3.3 at 192.168.1.4:51909' '' 14 TCP 55984->192.168.1.4:55984 'Deluge 1.3.3 at 192.168.1.4:55984' '' 15 UDP 55984->192.168.1.4:55984 'Deluge 1.3.3 at 192.168.1.4:55984' '' 16 TCP 63344->192.168.1.4:63344 'Deluge 1.3.3 at 192.168.1.4:63344' '' 17 UDP 63344->192.168.1.4:63344 'Deluge 1.3.3 at 192.168.1.4:63344' '' 18 TCP 61757->192.168.1.4:61757 'Deluge 1.3.3 at 192.168.1.4:61757' '' 19 UDP 61757->192.168.1.4:61757 'Deluge 1.3.3 at 192.168.1.4:61757' '' 20 TCP 62083->192.168.1.4:62083 'Deluge 1.3.3 at 192.168.1.4:62083' '' 21 UDP 62083->192.168.1.4:62083 'Deluge 1.3.3 at 192.168.1.4:62083' '' 22 TCP 51470->192.168.1.4:51470 'Deluge 1.3.3 at 192.168.1.4:51470' '' 23 UDP 51470->192.168.1.4:51470 'Deluge 1.3.3 at 192.168.1.4:51470' '' 24 TCP 53722->192.168.1.4:53722 'Deluge 1.3.3 at 192.168.1.4:53722' '' 25 UDP 53722->192.168.1.4:53722 'Deluge 1.3.3 at 192.168.1.4:53722' '' 26 TCP 62601->192.168.1.4:62601 'Deluge 1.3.3 at 192.168.1.4:62601' '' 27 UDP 62601->192.168.1.4:62601 'Deluge 1.3.3 at 192.168.1.4:62601' '' 28 TCP 64600->192.168.1.4:64600 'Deluge 1.3.3 at 192.168.1.4:64600' '' 29 UDP 64600->192.168.1.4:64600 'Deluge 1.3.3 at 192.168.1.4:64600' '' 30 TCP 64362->192.168.1.4:64362 'Deluge 1.3.3 at 192.168.1.4:64362' '' 31 UDP 64362->192.168.1.4:64362 'Deluge 1.3.3 at 192.168.1.4:64362' '' 32 TCP 58703->192.168.1.4:58703 'Deluge 1.3.3 at 192.168.1.4:58703' '' 33 UDP 58703->192.168.1.4:58703 'Deluge 1.3.3 at 192.168.1.4:58703' ''
For each entry to remove, you must give both the port and the protocol, in that order. cut
and awk
does a god job on each row:
upnpc -l | grep Deluge | cut -d "-" -f 1 | awk '{print $3,$2}' 51909 TCP 51909 UDP 55984 TCP 55984 UDP 63344 TCP 63344 UDP 61757 TCP 61757 UDP 62083 TCP 62083 UDP 51470 TCP 51470 UDP 53722 TCP 53722 UDP 62601 TCP 62601 UDP 64600 TCP 64600 UDP 64362 TCP 64362 UDP 58703 TCP 58703 UDP
Now, store each row as an element in a array, then print each element of that array to one space-delimited string, suitable as input to upnpc -d
.
elements=( `upnpc -l | grep Deluge | cut -d "-" -f 1 | awk '{print $3,$2}'` ) my_string=`for i in $(seq 0 $((${#elements[@]} - 1))); do echo -n ${elements[$i]} ""; done` echo $my_string 51909 TCP 51909 UDP 55984 TCP 55984 UDP 63344 TCP 63344 UDP 61757 TCP 61757 UDP 62083 TCP 62083 UDP 51470 TCP 51470 UDP 53722 TCP 53722 UDP 62601 TCP 62601 UDP 64600 TCP 64600 UDP 64362 TCP 64362 UDP 58703 TCP 58703 UDP
That is about it.
upnpc -d $my_string
So, what about arrays then?
$((${#elements[@]} - 1))
$elements
use something like this:for i in $(seq 0 $((${#elements[@]} - 1))); do echo -n ${elements[$i]} ""; done
References: Advanced Bash-Scripting Guide - Chapter 27. Arrays