Quantcast
Viewing all articles
Browse latest Browse all 10

Shell Script Wrapper Examples: Enhance the Ping and Host Commands

SShell script wrappers can make the *nix command more transparent to the user. The most common shell scripts are simple wrappers around the third party or system binaries. A wrapper is nothing but a shell script or a shell function or an alias that includes a system command or utility.

Linux and a Unix-like operating system can run both 32bit and 64bit specific versions of applications. You can write a wrapper script that can select and execute correct version on a 32bit or 64bit hardware platform. In clustered environment and High-Performance computing environment you may find 100s of wrapper scripts written in Perl, Shell, and Python to get cluster usage, setting up shared storage, submitting and managing jobs, backups, troubleshooting, invokes commands with specified arguments, redirecting stdout/stderr and much more.

In this post, I will explain how to create a shell wrapper to enhance the primary troubleshooting tool such as ping and host.

Why use shell script wrappers

  1. Time saving.
  2. Customize collection of *nix commands.
  3. Passing default arguments to binaries or 3rd party apps.
  4. Call to start the desired job.
  5. Wrappers are perfect when tools or command that require customized environmental variable settings, system controls or job-submission parameter.
  6. Useful in HPC, clustered environment, *nix sysadmin, and scientific research.

Example: Creating a shell script wrapper

The following shell script will start java app called kvminit by setting up system environment and redirect logs to a log file:

#!/bin/sh
# Wrapper for our kvm app called kvminit
export JAVA_HOME=${JAVA_HOME:-/usr/java}
export CLASSPATH="/home/vivek/apps/java/class
exec ${JAVA_HOME}/bin/java kvminit "$@" &>/var/log/kvm/logfile

Another wrapper script that can start / stop / nfs server or client in a single pass:

#!/bin/bash
# A shell script to start / stop / restart nfsv4 services
_me=${0##*/}              #Who am I? Server or client?
_server="/etc/init.d/rpcbind /etc/init.d/rpcidmapd /etc/init.d/nfslock /etc/init.d/nfs" # list of server init scripts
_client="/etc/init.d/rpcbind /etc/init.d/rpcidmapd /etc/init.d/nfslock" # list of client init scripts
_action="$1"            # start / stop / restart

# run all scripts with one action such as stop or start or restart
runme(){
	local i="$1"
	local a="$2"
	for t in $i
	do
		$t $a
	done
}

usage(){
	echo "$_me start|stop|restart|reload|status";
	exit 0
}

[ $# -eq 0 ] && usage

# main logic - take action
case $_me in
	nfs.server) runme "$_server" "$_action" ;;
	nfs.client) runme "$_client" "$_action" ;;
	*) usage
esac

Tools for troubleshooting: ping and host commands

As a sysadmin, I use basic troubleshooting tools such as ping and host frequently.

  • The ping commands help determine connectivity between devices on my network or remote network.
  • The host command help determine DNS problems. I can get information about Domain Name System records for specific IP addresses and/or host names so that I can troubleshoot DNS problems.

My problem with ping and host command

Both commands will not work when you pass protocol names or username:password from bash history:
curl -I http://www.cyberciti.biz/
ping !!:2

Sample outputs:

ping http://www.cyberciti.biz/
ping: unknown host http://www.cyberciti.biz/

OR
curl -I http://www.cyberciti.biz/
host !!:2

Sample outputs:

host http://www.cyberciti.biz/
Host http://www.cyberciti.biz/ not found: 3(NXDOMAIN)

So I decided to write a bash shell function ping() and host() that can use any one of the following format of domain and pass it to ping and host commands:

http://www.cyberciti.biz/
http://www.cyberciti.biz/file/one.html
scp://www.cyberciti.biz/foo
ftp://backup.cyberciti.biz/path
https://user:password@cp.cyberciti.biz/

_getdomainnameonly()

# Name: _getdomainnameonly
# Arg: Url/domain/ip
# Returns: Only domain name
# Purpose: Get domain name and remove protocol part, username:password and other parts from url
_getdomainnameonly(){
        # get url
	local h="$1"
        # upper to lowercase
	local f="${h,,}"
	# remove protocol part of hostname
        f="${f#http://}"
        f="${f#https://}"
	f="${f#ftp://}"
	f="${f#scp://}"
	f="${f#scp://}"
	f="${f#sftp://}"
	# Remove username and/or username:password part of hostname
	f="${f#*:*@}"
	f="${f#*@}"
	# remove all /foo/xyz.html*
	f=${f%%/*}
	# show domain name only
	echo "$f"
}

The $ character is used for parameter expansion, and command substitution. See how to use bash parameter substitution for more information.

ping() wrapper

# Name: ping() wrapper
# Arg: url/domain/ip
# Purpose: Send ping request to domain by removing urls, protocol, username:pass using system /bin/ping
ping(){
	local t="$1"
	local _ping="/bin/ping"
	local c=$(_getdomainnameonly "$t")
	[ "$t" != "$c" ] && echo "Sending ICMP ECHO_REQUEST to \"$c\"..."
	$_ping $c
}

Both ping() and host() [see below] are bash functions to performs a specific task.

host() wrapper

# Name: host() wrapper
# Arg: Domain/Url/IP
# Purpose: Dns lookups system /usr/bin/host
host(){
	local t="$1"
	local _host="/usr/bin/host"
	local c=$(_getdomainnameonly "$t")
	[ "$t" != "$c" ] && echo "Performing DNS lookups for \"$c\"..."
	$_host $c
}

Putting it all together

Create a shell script called $HOME/scripts/wrapper_functions.lib and put all above three functions as follows:

#!/bin/bash
## Note: Only works with bash 3.x or 4.x+ ##
_getdomainnameonly(){
	local h="$1"
	local f="${h,,}"
	# remove protocol part of hostname
        f="${f#http://}"
        f="${f#https://}"
	f="${f#ftp://}"
	f="${f#scp://}"
	f="${f#scp://}"
	f="${f#sftp://}"
	# remove username and/or username:password part of hostname
	f="${f#*:*@}"
	f="${f#*@}"
	# remove all /foo/xyz.html*
	f=${f%%/*}
	# show domain name only
	echo "$f"
}

ping(){
	local t="$1"
	local _ping="/bin/ping"
	local c=$(_getdomainnameonly "$t")
	[ "$t" != "$c" ] && echo "Sending ICMP ECHO_REQUEST to \"$c\"..."
	$_ping $c
}

host(){
	local t="$1"
	local _host="/usr/bin/host"
	local c=$(_getdomainnameonly "$t")
	[ "$t" != "$c" ] && echo "Performing DNS lookups for \"$c\"..."
	$_host $c
}

Edit your $HOME/.bashrc and append the following line:

source $HOME/scripts/wrapper_functions.lib

Save and close the file. You can load wrappers instantly by typing the following command:
$ source $HOME/scripts/wrapper_functions.lib
Test it as follows:

# First, just see header
curl -I http://cyberciti.biz/

## Now call it from history i.e. call 2nd arg passed to the curl ##
host !!:2

## Again call it from history i.e. call 1sat arg passed to the host ##
ping !!:1

## Other usage ##
host http://username:password@cyberciti.biz/
ping ftp://cyberciti.biz/foo

Sample outputs:

Image may be NSFW.
Clik here to view.
Bash wrappers example
Fig.01: Bash wrapper in action

How do I use real ping and host command

Simply use the following syntax:
/bin/ping -c4 cyberciti.biz
OR
/usr/bin/host cyberciti.biz ns1.example.com

How do I pass command line arguments via bash shell script wrapper?

Following modified bash code passes the command line arg to real system /bin/ping and /usr/bin/host:

#!/bin/bash
# Note: Works with bash 4.x and above only #
_getdomainnameonly(){
	local h="$1"
	local f="${h,,}"
	# remove protocol part of hostname
        f="${f#http://}"
        f="${f#https://}"
	f="${f#ftp://}"
	f="${f#scp://}"
	f="${f#scp://}"
	f="${f#sftp://}"
	# remove username and/or username:password part of hostname
	f="${f#*:*@}"
	f="${f#*@}"
	# remove all /foo/xyz.html*
	f=${f%%/*}
	# show domain name only
	echo "$f"
}


ping(){
	local array=( $@ )  		# get all args in an array
	local len=${#array[@]}          # find the length of an array
	local host=${array[$len-1]}     # get the last arg
	local args=${array[@]:0:$len-1} # get all args before the last arg in $@ in an array
	local _ping="/bin/ping"
	local c=$(_getdomainnameonly "$host")
	[ "$t" != "$c" ] && echo "Sending ICMP ECHO_REQUEST to \"$c\"..."
	# pass args and host
	$_ping $args $c
}

host(){
	local array=( $@ )
	local len=${#array[@]}
	local host=${array[$len-1]}
	local args=${array[@]:0:$len-1}
	local _host="/usr/bin/host"
	local c=$(_getdomainnameonly "$host")
	[ "$t" != "$c" ] && echo "Performing DNS lookups for \"$c\"..."
  	$_host $args $c
}

Run it as follows:

curl -I http://cyberciti.biz/
ping -v -c 2 !!:2
host -t aaaa !!:4
host -t mx !!:3
host -t a ftp://nixcraft.com/
host -a ftp://nixcraft.com/
ping 8.8.8.8
host 8.8.8.8

Sample outputs:

Image may be NSFW.
Clik here to view.
Bash wrapper with command line args
Fig.02: Bash wrapper with command line args in action

See how to find the last argument passed to a shell script and extract all command line arguments before last parameter in $@ for more information.

Conclusion

In this example, I wrote a small script and functions, that do a minimal operation, and then calling then in another script or directly to achieve my result. This is the basis of Unix and Linux. You need to reuse code and existing binaries to create desired functionality. Have a favorite wrapper script? Let’s hear about it in the comments.

The post Shell Script Wrapper Examples: Enhance the Ping and Host Commands appeared first on nixCraft.


Viewing all articles
Browse latest Browse all 10

Trending Articles