bash provides a function mechanism by which you can define and recall complex command sequences. Functions can process command line arguments, perform iteration (loops), and execute a number of complex conditional statements.
A function is similar to an alias. It is first defined, assigned a name, and stored in the shell's environment like a variable. Or it may be declared at the beginning of a shell script and invoked when needed.
It is invoked by its name and may take options and arguments if so designed. The command interpreter then expands and runs the commands defined in the function as if they were entered on the command line.
We will look at using iteration and complex conditional statements in a function in a later module. In this module we will look at the basic structure and rules for a function and how it overcomes some of the limitations of the alias.
function_name () { commands } function function_name { commands } and both styles combined : function function_name () { commands }
The function_name is a single word which may be composed of a combination alpha-numeric characters and the underscore. Using other characters or starting the function's name with a digit is not recommended. Using the same name as an existing command is possible but can create an endless recursive loop if done improperly.
In the first form of definition above, the parenthesis and the left brace after the function name are required. However, the spacing between them is not. Once you have typed this in, press [enter].
The standard prompt will be replaced by the PS2 prompt indicating that more input is needed to complete the command (defining the function). Enter the command sequence the function should execute.
You may use piping and redirection as desired. You may use &&, ||, and () to create simple conditional statements. And you may enter multiple commands on separate lines. It is possible for a function to run 100 lines.
You may use the command line parameters to reference arguments specified on the command line after the function name when invoking. We will look at working with command line arguments later in this module.
When you have finished entering the commands, enter the closing or right brace on its own line to signify end of function definition.
Exercise : #( Enter the following at the prompt. ) lsl () { ls -l $* | less } #( Run the function entered. ) lsl #( If desired, remove the function definition from the environment. ) unset lsl |
There is one difference between the 1st two forms. If the command is already defined as an alias, the 1st form will not allow you to use an existing command's name as a function name. However, the 2nd form will allow it.
If you have defined an alias and a function by the same name, upon invoking the name, the alias will be executed. If you want both alias and function to run, call the function from inside the alias. In general, if you plan on defining a function, don't define an alias of the same name.
It is possible to define the whole function on a a single line, but you must use an additional semi-colon to delimit the last command from the closing brace.
function lsl { ls -l $* | less; }
Like a variable, a function is a local definition and is not exported unless it is marked as exportable. To mark a function as exportable, use :
declare -xf func_name
or
export -f func_name
You may also mark a function as readonly which will prevent it from being redefined.
declare -fr func_name
or
readonly -f func_name
-f = function option is required so the commands are applied to the function name and not a variable of the same name.
We have already used a parameter reference in the lsl example above.
To access arguments passed into a function, use the standard command line parameters, $0-9..., $*, $@, and $#.
Exercise : #( Define the following function. ) function arglist { echo "my list '$*'" echo "argument count $#" echo $2; echo $1 } #( Now run it with at least two arguments. A quoted string will be treated as a single argument)
arglist gnu "is not" unix |
Arguments specified with a function call exist only in the parameter list of the running function. Each time you call the function, a new parameter list is generated and assigned the arguments following the invoked function name. You cannot normally change the parameters passed in but you can assign them to variables inside function if you need to manipulate the values passed.
Although a function has its own parameter list, it still has access to both global and local variables defined in the shell in which it was called. And if a function creates a variable or changes its contents, these changed values and variables are available to the shell after the function completes.
Exercise : #( Make sure variables are unassigned. ) unset outsider unset insider #( Create the function. ) function changer { echo "Inside function before changes : " echo "outsider : $outsider" echo "insider : $insider" outsider="reassigned inside" insider="created inside" echo "Inside function after changes : " echo "outsider : $outsider" echo "insider : $insider" } #( Create the local variable outside the function. ) outsider="assigned outside" #( Print the two variables we are experimenting with. ) echo "$outsider" echo "$insider" #( Call the function ) changer #( Show contents after function call ) echo "$outsider" echo "$insider" |
If an existing variable is tagged readonly, a function can change its value.
It is also possible to keep a new variable or the change to an existing variable local to the function's execution with the command local.
Exercise : #( Make sure variables are unassigned. ) unset mvar unset lvar #( set a variable in shell at prompt. ) mvar="from main" #( Create the function. ) function lchange { #( define mvar and lvar as locals and show) local mvar="mvar in lchange" local lvar="lvar in lchange" echo $mvar echo $lvar } echo "Before lchange call" echo $mvar echo $lvar lchange # ( display mvar and lvar after function call ) echo $mvar echo $lvar |
Because a called function does execute, it returns a status. You may control the status returned with the return command. This allows you to use %amp;&, || and other shell commands to conditionally act on completion of the function call.
If you use parenthesis in a function and you set a return code from within the parenthesis with the intention of terminating the function, you will have to take additional steps to terminate the function. Remember that parenthesis create a sub-shell condition. Use the conditional with its own copy of variables. The same return code is only communicated to the next command on the line. Remember,
Exercise : #( Create a function that returns a status of 0 or success ) function good { return 0 } #( Create a function that returns a status of > 0 or failure ) function bad { return 1 } #( Call functions with the AND an OR delimiters ) good && echo "success" good || echo "failure" bad && echo "success" bad || echo "failure" #( Create two functions. Both should terminate with fail status. ) #( Create a function with the return command inside a parenthesis ) function pbadb { ( return 1 ) echo return failed } #( Create a function with the return command inside a parenthesis but with an added conditional invocation of a return outside. ) function pbadg { ( return 1 ) || return 1 echo return failed } #( Invoke the two functions with a conditional message based on returned status. ) pbadb || echo "failure" pbadg || echo "failure" |