Beginners Guide to Creating User-Defined Functions in a Shell Script

Functions in the Shell

A function is a set of one or more statements that act as a complete routine. Each function must have a unique name within that shell or shell script.

Syntax

The following is the function syntax in the Bourne shell:

function_name ()
{
        block_of_statement_lines
}

The following is the function syntax in the Korn shell:

function function_name
{
        block_of_statement_lines
}

The following is the function syntax in the Bash shell:

function function_name [ block_of_statement_lines
]

Function Execution

A function is executed within the shell in which it has been declared; the function is executed as a subroutine of the shell. It is not executed as a subprocess of the current shell. After a function has been loaded into the current shell, it is retained.

Positional Parameters and Functions

Functions act like mini-scripts. They can accept parameters that are passed to them, they can use local variables, and they can return values back to the calling shell command line. Positional parameters passed to the function are not the same positional parameters that are passed to the script; for example:

$ cat funparas.ksh
#!/bin/ksh

# Script name: funparas.ksh

function hello
{
    print '$1 in the function is: ' $1
}

print 'Input passed and stored in $1 is: ' $1

hello John              # execute the function hello

print
print 'After the function $1 is still ' $1
$ ./funparas.ksh Susan
Input passed and stored in $1 is:  Susan
$1 in the function is:  John

After the function $1 is still  Susan

The following customized .kshrc file shows three functions: killit, rcgrep, and rgrep. The following examples show the execution of each of the three functions.

$ cat /.kshrc

function killit         # Korn shell syntax
{
    pkill -u $1
    print -n "Had to kill process for user: $1 "
    print "on $(date +%D) at $(date +%T)"

    # The previous print statement may be appended to a log file.
}

function rcgrep         # Korn shell syntax
{
    grep $1 /etc/init.d/* | more
}

rgrep ()                # Bourne shell syntax
{
    find $2 -type file -exec grep $1 {} \; | more
}

The following example calls the killit function, which calls pkill to kill all processes owned by the user-specified as the first argument to the function. It then prints a message that is logged to a file.

$ killit annette
Had to kill process for user: annette on 05/23/00 at 21:38:32

The following example calls the killit function, which calls pkill to kill all processes owned by the user specified as the first argument to the function. It then prints a message that is logged to a file.

$ rcgrep sed
/etc/init.d/autoinstall:# are available, then the default profiles in are used.
/etc/init.d/dtlogin:# This version of the dtlogin.rc script can be used on the Solaris(TM)
/etc/init.d/inetinit:# tcp used when it loaded.
...

The following example performs a recursive grep search. Two arguments are passed to the rcgrep function: the first is the search string, and the second is the directory from which to begin the search. The find statement recursively finds all files under that directory, and passes each file name to the grep statement, which then searches for the search string within each file. Therefore, you can apply grep to the contents of the files, rather than to the file names.

$ rgrep root /etc/default
/etc/default/login:# If CONSOLE is set, root can only login on that device.
/etc/default/login:# Comment this line out to allow remote login by root.
/etc/default/login:# SUPATH sets the initial shell PATH variable for root
/etc/default/login:# to log all root logins at level LOG_NOTICE and multiple failed login
/etc/default/su:# CONSOLE determines whether attempts to su to root should be logged
/etc/default/su:# SUPATH sets the initial shell PATH variable for root
/etc/default/su:# root, LOG_INFO messages are generated for su's to other users, and LOG_CRIT

Return Values

A value can pass from a function back to the shell or script that called that function. The return statement terminates the function and passes a value back to the calling shell or script. The return statement returns any designated value between 0 (zero) and 255. By default, the value passed by the return statement is the current value of the ? exit status variable.

The typeset and unset Statements

The following shows the syntax for using typeset and unset with functions:

  • typeset -f lists the known functions and their definitions
  • typeset +f lists the known function names
  • functions is an alias for typeset -f
  • unset -f name unsets the value of the function

The following are examples of using typeset and unset with functions:

$ typeset -f
function killit
{
       pkill -u $1
       print -n "Had to kill process for user: $1 "
       print "on $(date +%D) at $(date +%T)"

     # The previous print statement may be appended to a log file.
}

function rcgrep
{
       grep $1 /etc/init.d/* |more
}

function rgrep
{
      find $2 -type file -exec grep $1 {} \; | more
}

The following function will echo its purpose and the line it will execute:

# System call count by process
function syscall_count_by_process
{
   echo "Syscall count by program"
   echo "dtrace -n 'syscall:::entry { @num[execname] = count(); }'"
   dtrace -n 'syscall:::entry { @num[execname] = count(); }'
$ typeset +f
killit
rcgrep
rgrep
$ alias | grep fun
functions=’typeset -f’
$ unset -f rcgrep
$ typeset +f
killit
rgrep

Function Files

You can create functions within a shell script, or they can be external to the shell script. Functions created within a shell script exist only within the shell interpreting that script. You can put functions in a file. When you create a function, it must contain the definition of only one function. The name of a function and function file must be the same. Make a function file an executable. A function file can be autoloaded into a shell script and used by that script. Only one function can be in each function file; for example:

$ cat holder

function holder
{
    print
    print -n "Type some text to continue: "
    read var1
    print "In function holder var1 is: $var1"
}

Autoloading Korn Shell Functions With the FPATH Variable

A function is treated in the same way as a Korn shell built-in statement. When the function is invoked, it is invoked in the current shell and not as a subprocess. Before a function is invoked, the shell must know that the function exists. Therefore, you must define functions at the start of the shell script before any command-line command attempts to invoke the function.

If a function was created as a function file, you must create or modify the FPATH variable to include the directory name that contains the function file. Declare the FPATH variable before any command-line command attempts to invoke a function from one of the function files.

Directories that are listed in the FPATH variable should contain only function files. The FPATH variable is an environment variable; for example:

$ FPATH=$HOME/function_dir ; export FPATH

The files that exist in the $HOME/function_dir directory should all be function files. Ensure that no other type of files reside in that directory. By using the FPATH variable, the functions can be autoloaded into a shell script and do not need to be declared in every script. Also, by updating a function file (stored in a directory that is pointed to by the FPATH variable), any scripts that use that function autoload the updated version of the function file when the scripts are next invoked.

$ cat holdertest.ksh
#!/bin/ksh

# Script name: holdertest.ksh

FPATH=./funcs
export FPATH

print "Calling holder..."
holder

print
print "After the function var1 is: $var1"
$ ./holdertest.ksh
Calling holder...

Type some text to continue: shell scripts
In function holder var1 is: shell scripts

After the function var1 is: shell scripts