## 35.30. Using Shell Functions in Shell Scripts

So far, we have discussed some shell function basics (Section 29.11), using examples such as the `mx( )` function that uses sed and dig to find out what host accepts mail for a given address. In that example, we simply made a set of complex functionality available as a quick and easy (and short) call from the command line. But you can also define functions and use them within shell scripts, or you can use the . and `source` commands to include those functions from an external file (Section 35.29).

We've also discussed using functions to automate repetitive tasks (Section 29.11), such as calculating factorials.

For now, let's demonstrate both of these approaches specifically with respect to defining a function to automate a repetitive task and then sharing the function with other shell scripts. Using the `mx( )` function we defined earlier, let's put it into its own file, mx.sh, and store it in our personal shell function library directory (in this case, $HOME/lib):

>     $ cat > ~/lib/mx.sh
>     function mx( ) {
>     # Look up mail exchanger for host(s)
>     for host
>     do
>         echo "==== $host ===="
>         dig "$host" mx in |
>         sed -n '/^;; ANSWER SECTION:/,/^$/{
>                 s/^[^;].* //p
>         }'
>     done
>     }
>     ^D
>     $ more !$
>     function mx( ) {
>     # Look up mail exchanger for host(s)
>     for host
>     do
>         echo "==== $host ===="
>         dig "$host" mx in |
>         sed -n '/^;; ANSWER SECTION:/,/^$/{
>                 s/^[^;].* //p
>         }'
>     done
>     }
>     $

Now the file ~/lib/mx.sh contains a function named `mx( )` -- fair enough, but let's say we want to be able to pass a list of hosts (determined dynamically on a regular basis, say, from spam-fighting tools that find open SMTP proxies) to a shell script, and have that shell script email a form letter to the `postmaster` address at that host. We will call the shell script proxynotify, and call it as follows:

>     $ proxynotify < proxyList

`proxylist` contains a list of hosts, one per line, in the `com` domain. We want to iterate over them and mail the postmaster for the domain, rather than mailing directly to an address such as `postmaster@[``IP``]`, on the assumption that maybe the top-level postmaster can fix what may be an unmonitored relay. Just to verify that some other system isn't responsible for delivering the mail, we will check using the `mx( )` shell function. We've also included a quickie shell function named `ip( )` that returns the IP address for a given hostname. As you can see, we use a local variable for the IP address, and we use the `-z` test for zero length of a string. We also check whether the file is readable, check the script arguments, and use a variety of other tricks.

>     #!/bin/sh
>     # proxynotify demo

>     # get our function
>     . $HOME/lib/mx.sh

>     function ip( ) {
>         for host
>         do
>             local ip=`dig in host $host |\
>             grep $host |\
>             grep "TABATAB" |\
>             awk '{print $5}'`
>             echo $ip
>         done
>     }

>     if [ -z "$1" ]
>     then
>         echo "Usage: $0 [file]"
>         exit 1
>     elif [ -r "$1" ]
>     then
>         echo "found a file and it is readable"
>     else
>         echo "file $1 not readable or does not exist"
>         exit 1
>     fi

>         for domain in `cat "$1"`
>         do
>         echo "processing $domain"
>         themx=`mx $domain`
>         echo "MX for $domain is '$themx'"
>         if [ ! -z $themx ]
>         then
>             cat formletter | mail -s "proxy" postmaster@$themx
>         else
>             echo "couldn't find MX for $domain,"
>             echo "mailing direct-to-IP instead."
>             theip=`ip $domain`
>             if [ ! -z $theip ]; then
>                 cat formletter | mail -s "proxy" postmaster@$theip
>             else
>                 echo "giving up, can't find anyone to notify"
>                 echo "$domain" >> /tmp/proxybadlist.$$
>                 return 1
>             fi
>        fi
>     done
>     mail -s "bad proxies" <</tmp/proxybadlist.$$
>     rm /tmp/proxybadlist.$$

-- SJC

Copyright © 2003 O'Reilly & Associates. All rights reserved.