Gateway / Router Detection with Bash: Functions and Variables

3

I'm drafting / still brainstorming / a bash shell script. The aim of which, is to; help the user to:

  • Interface with various CLI tools.
  • Perform various administrative tasks.

It might, eventually, evolve into a bit of a personalized framework.


A particular requirement that it has:

  • It should be able to detect the IP address of the gateway / router on the current LAN.
  • I've come up with route -n |awk '/UG/{print $2}'; to echo the corresponding IP to stdout.

Should I use that input as a function(){ }, it's output as a $variable, or both??


Here are a few simple ideas that I've had:

  1. #function#
    gw() { route -n |awk '/UG/{print $2}'; }

  2. #variable#
    gw="$( route -n |awk '/UG/{print $2}'; )"

  3. #function#
    gw() { route -n |awk '/UG/{print $2}'; }
    
    #variable#
    gw="$( route -n |awk '/UG/{print $2}'; )"

  4. #function#
    gw() { route -n |awk '/UG/{print $2}'; }
    
    #variable#
    gw="$(gw)"

voices

Posted 2016-06-05T18:07:39.937

Reputation: 2 053

Answers

2

I completely over answered your question, but hopefully it's helpful. I touched on some general stuff, and then specifics on the default gateway, and finally some other examples.

Here are some general comments:

  • I suggest that you do form those calls into functions which among other things will allow you to build up your functions such that higher level functions can simply be wrappers around one or more lower level functions.

  • For handling the output from a function, I find that using stdout is the most versatile method, and it lends itself to the modular / wrapper approach of building up functions.

If you do variable assignment and export from within a function, scope issues will cause you problems.

If you have a function that assigns itself to a variable inside the same function; e.g.

`testFx() { export routeInfo="$(route)"; }`

If the variable assignment to routeInfo by 'testFx' is in the same scope as the caller - in other words if the function is declared in the current shell, and called from the current shell, the resultant assignment to variable routeInfo will be accessible from that shell. If the function is used in an executed script, the result will not be accessible to the shell that called the script, although the function will be accessible.

This is a bit of an oversimplification, but I think it clarifies the scope issue well. Objects in scope to a parent are accessible to a child shell/process, but not vice versa. It is difficult to pass things 'back up' the line (in bash).

This difference is exposed in the different behavior seen when sourcing a script file vs. executing a script file.

The executed script runs in a sub / child shell; the sourced file is added to the local environment in the current context; not in a child / sub shell.

You can test what I am saying by adding that simple function to your shell - simply paste it in (or add to .bashrc and reload shell, etc.)

Then call the function and try to echo the variable holding the value, echo $routeInfo. That will work as expected.

Clear out that variable with unset routeInfo

Then create a simple script as follows:

vi ~/test.sh

with contents:

#!/bin/bash
testFx

Save, close, and add execute permissions chmod +x ~/test.sh

Then run the script ~/test.sh from a shell that has this function defined in it.

After running the script, echo the variable that should be holding the value: echo $routeInfo, and you'll see it's empty - assuming you made the unset call above; because the export was unable to ascend to the parent shell / process.

Because of this scope issue and the lack of a traditional 'return' value from a bash function, the way I typically see the 'result' value from a bash function used is either directly; by letting it output to stdout, or capturing it in a variable. For either case the function itself is written the same way, only the manner in which it is called gets changed.

Here are some examples of 'grabbing' output values from bash functions

rslt=$(testFx)
If you can expect multiple results, you can store the results in an array:
rslt=($(testFx)) with ${#rslt[@]} containing the number of entries (N), and you can access the elements either by direct indexing:
${rslt[$a]} with a = 0 to N-1
or via a loop:
for i in ${rslt[@]}; do echo "$i"; done

Hopefully that addressed a good part of your question. I'm throwing in a few network data collection functions I have on my local machine. If you are looking for any other specific information let me know, I probably have a function for it.

getExternalIP () {
    lynx -dump -hiddenlinks=ignore -nolist http://checkip.dyndns.org:8245/ | grep "Current IP Address" | cut -d":" -f2 | cut -d" " -f2
}

getLanIP () {
    ip addr show | grep -E -v '127\.0' | grep -Eo '^.*brd' | grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
}

getDefGW_IP () { 
    route -n | awk '/UG/ {print $2}'; 
}

getDefGW_Host () { 
    route  | awk '/default/ {print $2}'; 
}    

getDefGW_Host_and_IP () { 
    echo "Def. GW hostname / IP Address: $(getDefGW_Host) / $(getDefGW_IP)";
}

Here's a helpful regex string to use when working with IP addresses:

iprgx='(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'

Example using that - a function to output all IP addresses on your LAN that you have seen ARP packets from:

getARPTable_IPs() {
    arp -na | grep -Eo "$iprgx";
}

Store the results in an array:

arpIPs=($(getARPTable_IPs))

Lastly, for a modern linux distro, here are the commands and file system locations that I frequently use to get network info / status. Older commands like netstat and ifconfig are deprecated and for good reasons.

  1. ip
  2. ss
  3. Under /sys/class/net will be a directory for each NIC in your system filled with files containing network information.
    a. Many of the command line utilities simply use this output and reformat it.
    b. /proc/net has a great deal of info.
    c. For instance, the arp -na command can be entirely replicated by cat /proc/net/arp
  4. iptables

Argonauts

Posted 2016-06-05T18:07:39.937

Reputation: 4 000

The more info; the better. Thanks for contributing, I will read it in full a bit later. Much appreciated. – voices – 2016-06-07T12:34:45.033