>

BASH: Building an Arsenal‎ – Part 1

perl-banner

If you spend much time at all coding or scripting you realize that you often do some of the same things over and over. That’s where functions and libraries come in. Having a library of commonly used functions handy helps speedup the coding process quite a bit.

Usually I prefer to use the best tool for the job. That means using Perl, PHP, C, C++, BASH, or any other language suited for the task. However, lately I’ve been using BASH more than usual. Primarily because it’s pretty much guaranteed to be on just about ever, non Windows, system that I touch. Unfortunately, while I have an extensive library of old scripts to pull from, I’ve never really pulled together an easily accessible library of common functions. As a result I’ve been spending a little time pulling old functions out of old scripts and building my own set of bash libraries.

One of the first things I often need to do is figure out what platform my script is running on. I used to deal with a variety of UNIX versions. SunOS, Solaris, AIX, HP-UX, and IRIX where the main ones I used daily. Lately, I’m really only concerned with Linux and OS X. I still use the same function, get_platform(), to figure out the platform my script is running on.

get_platform() {
if [ -x "/bin/uname" ]; then
    echo $(/bin/uname)
elif [ -x "/usr/bin/uname" ]; then
    echo $(/usr/bin/uname)
else
    echo "Unknown"
fi
}

I combine this with a simple case statement to handle platform specific differences.

case $(get_platform) in
Darwin)
    echo "Running on a Mac"
;;
Linux)
   echo "Running on Linux"
;;
*)
   echo "Running on something else"
   exit 1
;;
esac
}

I actually use this in a lot of other functions. While this method is not the most efficient for execution purposes, it is for reusability and ease of coding.

At this point the other thing I should mention is that during development I will typically just include my whole library, by “sourcing” it into my script. If it’s something that I want to be standalone, one the of last parts of my development process is to just copy the functions I use directly into the script. To just load (or source) the whole library I use the following line of code near the top of my script:

. /usr/local/lib/libBash

Now for the meat of things. Let’s look at some of the network related functions I have in my library.

is_ifconfig_new()

The is_ifconfig_new() function was added specifically for Fedora compatibility. Fedora is using a version 2 beta of ifconfig which changes the format of the output. This broke several of my functions and I had to update them to watch for this.

is_ifconfig_new() {
#Check if using version 2.x of ifconfig. Ifconfog 1 & 2 have different outputs
	ver=$(/sbin/ifconfig --version 2>&1 | grep net-tools | awk '{ print $2 }')
    if (( "${ver%%.*}" < "2" )); then 
	    return 1
	else
		return 0
     fi
}

get_interface()

The get_interface() function attempts to identify the active interface that is used to connect to the internet. In the case of multiple active interfaces it will only return one of them. It does this by checking the routing table to see which interface is used to access an internet address. I typically use 8.8.8.8 which is Google’s public DNS pool address.

get_interface() {
# Example:
# INTERFACE=$(get_interface)
# echo "My active network interface is $INTERFACE"

case $(get_platform) in
Darwin)
    echo "$(route get 8.8.8.8 | awk '/interface/ { print $2 }')"
;;
Linux)
	echo "$(/sbin/ip route get 8.8.8.8 | awk 'NR==1 {print $5 }')"
;;
*)
    echo "Unknown"
    ;;
esac
}

get_MAC()

The get_Mac() function simply returns the MAC address of the specified interface.

get_MAC() {
# get_MAC $interface
# Example:
# MAC=$(get_MAC $INTERFACE)
# echo "My MAC address is $MAC"

case $(get_platform) in
Darwin)
    echo "$(/sbin/ifconfig $1 | awk '/ether/ {print $2}')"
;;
Linux)
    if  is_ifconfig_new; then 
		echo "$(/sbin/ifconfig $1 | awk '/ether/ { print $2 }')"        
    else
		echo "$(/sbin/ifconfig $1 | awk '/HWaddr/ { print $5 }')"

    fi
;;
*)
    echo "Unknown"
    ;;
esac
}

get_ip4()

The get_ip4() function returns the IP address of the specified interface.

get_ip4() {
# get_ip4 $interface
# Example:
# IP4=$(get_ip4 $INTERFACE)
# echo "My IP Address is $IP4"

case $(get_platform) in
Darwin)
    echo "$(/sbin/ifconfig $1 | awk '/inet / {print $2}')"
;;
Linux)
	echo "$(/sbin/ifconfig $1 | grep inet | sed 's/addr://' | awk 'NR==1 { print $2 }')"
;;
*)
    echo "Unknown"
    ;;
esac
}

get_ip6()

The get_ip6() function returns the IPv6 address of the specified interface.

get_ip6() {
# get_ip6 $interface
# Example:	
# IP6=$(get_ip6 $INTERFACE)
# echo "My IPv6 Address is $IP6"

case $(get_platform) in
Darwin)
    echo "$(/sbin/ifconfig $1 | awk '/inet6 / {print $2}')"
;;
Linux)
    if  is_ifconfig_new; then 
		echo "$(/sbin/ifconfig $1 | awk '/inet6/ { print $2 }')"
	else
    	echo "$(/sbin/ifconfig $1 | awk '/inet6/ { print $3 }')"
   fi
;;
*)
    echo "Unknown"
;;
esac
}

get_netmask()

The get_netmask() function returns the current netmask, in decimal, of the specified interface.

get_netmask() {
# get_netmask $interface
# Example:	
# NETMASK=$(get_netmask $INTERFACE)
# echo "My Netmask is $NETMASK"

case $(get_platform) in
Darwin)
    netmask="$(/sbin/ifconfig $1 | sed 's/0x//'   | awk '/netmask / {print $4}')"
# OS X returns the netmask in hex format and it needs to be converted to dec.
    printf "%d.%d.%d.%d\n" 0x${netmask:0:2} 0x${netmask:2:2} 0x${netmask:4:2} 0x${netmask:6:2}
;;
Linux)
    if  is_ifconfig_new; then 
	    echo "$(/sbin/ifconfig $1 | awk '/netmask/ { print $4 }')"
    else
		echo "$(/sbin/ifconfig $1 | awk -F':' '/Mask:/ { print $4 }')"
	fi
;;
*)
    echo "Unknown"
;;
esac
}

get_network()

The get_network() function returns the network that is in use, based on an IP address and a netmask.

get_network() {
# get_network $ipadddress $netmask
# Example:
# NETWORK=$(get_network $IP4 $NETMASK)
# echo "My network is $NETWORK"

	IFS=. read -r a1 a2 a3 a4 <<< "$1"
	IFS=. read -r m1 m2 m3 m4 <<< "$2"
	printf "%d.%d.%d.%d\n" "$((a1 & m1))" "$((a2 & m2))" "$((a3 & m3))" "$((a4 & m4))"

}

get_broadcast()

The get_broadcast() function returns the broadcast address that is in use, based on an IP address and a netmask.

get_broadcast() {
# get_broadcast $ipadddress $netmask
# Example:
# BROAD=$(get_broadcast $IP4 $NETMASK)
# echo "My Broadcast Address is $BROAD"

	MASK="255"
	IFS=. read -r a1 a2 a3 a4 <<< "$1"
	IFS=. read -r m1 m2 m3 m4 <<< "$2"
	printf "%d.%d.%d.%d\n" "$((a1 | $(($MASK ^ m1))))" "$((a2 | $(($MASK ^ m2))))" "$((a3 | $(($MASK ^ m3))))" "$((a4 | $(($MASK ^ m4))))"
}

get_numbits()

The get_numbits() function returns the number of bits used in a netmask, in other words the netmask in CIDR format.

get_numbits() {
#get_numbits $netmask
# Example:
# NUMBITS=$(get_numbits $NETMASK)
# echo "Numbits of the netmask is $NUMBITS"

	numbits=0
	IFS=.
	for oct in $1 ; do
        case $oct in
            255) let numbits+=8;;
            254) let numbits+=7;;
            252) let numbits+=6;;
            248) let numbits+=5;;
            240) let numbits+=4;;
            224) let numbits+=3;;
            192) let numbits+=2;;
            128) let numbits+=1;;
            0);;
            *) echo "Error: $oct is not recognised"; exit 1
        esac
    done
    echo "/$numbits"
}

Putting it all together

So when combined into a single library these can be used like this:

#!/bin/bash
#include BASH library
. /usr/local/lib/libBash
INTERFACE=$(get_interface)
echo "My active network interface is $INTERFACE"
MAC=$(get_MAC $INTERFACE)
echo "My MAC address is $MAC"
IP4=$(get_ip4 $INTERFACE)
echo "My IP Address is $IP4"
IP6=$(get_ip6 $INTERFACE)
echo "My IPv6 Address is $IP6"
NETMASK=$(get_netmask $INTERFACE)
echo "My Netmask is $NETMASK"
NETWORK=$(get_network $IP4 $NETMASK)
echo "My network is $NETWORK"
BROAD=$(get_broadcast $IP4 $NETMASK)
echo "My Broadcast Address is $BROAD"
NUMBITS=$(get_numbits $NETMASK)
echo "Numbits of the netmask is $NUMBITS"

The results looks something this:

$ ./testlib 
My active network interface is en0
My MAC address is 3c:07:54:1f:f2:33
My IP Address is 10.1.10.214
My IPv6 Address is fe80::3e07:54ff:fe1f:f233%en0
My Netmask is 255.255.255.0
My network is 10.1.10.0
My Broadcast Address is 10.1.10.255
Numbits of the netmask is /24
banner ad

Comments are closed.