CSC128: Introduction to UNIX

Functions


Functions

Just like many other programming languages, the Bourne shell and bash have a way for the user to define functions.  

Functions can be very helpful in script writing.  Many times, the same thing needs to be done in many places:
#!/bin/sh

if [ $# -lt 2 ]
then
  echo "ERROR: you need at least 2 arguments"
  exit 1
fi

ls $1
if [ $? -ne 0 ]
then
  echo "ERROR: could not read the directory: $1"
  exit 1
fi

if test -e my_file
then
  echo "ERROR: my_file does not exist"
  exit 1
fi
In each of these if then fi statements, an error message is echoed to stdout, and the script is immediately exited with an exit code of 1.  Another script might have 50 or 100 places that need to do the same thing under different error conditions.   To generalize the task of giving an error message and exiting, we might want to write in as a function:
#!/bin/sh

error_fn()
{
  echo "ERROR: $1"
  exit 1
}

if [ $# -lt 2 ]
then
  error_fn "
you need at least 2 arguments "
fi

ls $1
if [ $? -ne 0 ]
then
  error_fn "could not read the directory: $1"
fi

if test -e my_file
then
  error_fn "my_file does not exist "
fi
Another advantage to this arrangement is that if you should decide to change the error handling routine, you only have to change in in one place, within the function definition, rather than everywhere in the script file.

There are two ways to define a function;  you will likely see them both used.
function function_name
{
  commands...
}

function_name ()
{
  commands...
}

Both these forms perform the same way.  Sometimes, people place the opening brace on the same line as the function name:
function_name () {
  commands...
}
After a function has been defined, the shell treats it as a built-in command.  Arguments are passed to the function the same as they are to the script. Consider the following:

#!/bin/sh

my_fn()
{
  echo "The first argument to my_fn is: $1"
}

echo "The first argument to this script is: $1"

my_fn "bar"


echo "The first argument to this script is still: $1"

If this script is executed on the command line:
user@host:~$ myscript.sh foo
The first argument to this script is: foo
The first argument to my_fn is: bar
The first argument to this script is still: foo

user@host:~$
Notice that the argument list $1 - $9 is locally redefined only within the function to the be the arguments to the function, rather than to the script.

Note:
The function defintion must come before any calls to its function.



Toolbox

the "here" document p. 394
To redirect the contents of a text file to the stdin of a program, we use the form:
program < file.txt
Then, rather than receiving input from the user, program takes its input from file.txt .  

Sometimes within a script file, it is desirable to control a program that doesn't take all its input on the command line, but that reads commands from stdin.  (A good example of this is ftp .)  At the same time, we want those commands to be written in the shell script itself, so as to take advantage of locally defined variables, etc.  

To do this, we use a special for of redirection, called the here document.  An example:
ftp <<+
open duracef.shout.net
shum
binary
get $myvar
bye
+
This will execute the program ftp , sending it the commands listed between the +'s.  Any delimiting string can be used:
ftp <<EOF
open duracef.shout.net
shum
binary
get $myvar
bye
EOF
As soon as the delimiting string defined after the << is encountered on a line by itself, the here document is ended, and any further lines are interpreted as shell commands.

Note that the password is not in the here document.  This is because passwords are typically not read in the normal way from stdin, for security reasons. They are read directly from the controlling tty (terminal input).  

cal
This command displays a calendar. See man cal for more information.