Lesson 13
• Shell Fuctions
• Using functions
• Pass Parameters to a Function
• Returning Values from Functions
• Nested Functions
Shell functions
Functions enables to break down overall functionality of a script into smaller,
logical subsections, which can be called to perform their individual task.
• Using functions makes the scripts easier to maintain and performs repetitive tasks
is a way to create code reuse.
Shell functions are similar to subroutines, procedures, and functions in other languages.
They differ because they return a status code instead of a return value.
Without a return value, they cannot be used in expressions.
Shell functions
Creating Functions
We can create functions to perform tasks and we can also create functions that take
parameters (also called arguments)
To declare a function use syntax:
function fname()
{
statements;
}
Alternately
fname()
{
statements;
}
A function can be invoked just by using its name:
$ fname ; # executes function
Arguments can be passed to functions and can be accessed by our script:
fname arg1 arg2 ; # passing args
Using functions
#!/bin/sh
# Define your function here
Hello () {
echo "Hello World"
}
# Invoke your function
Hello
example
$./test.sh
Hello World
$
Example result
Using functions
#!/bin/bash
hello()
{
echo “You are in function hello()”
}
echo “Calling function hello()…”
hello
echo “You are now out of function hello()”
example
We called the hello() function by name by using the line: hello.
When this line is executed, bash searches the script for the line hello(). It finds it right at
the top, and executes its contents.
Example result
Pass Parameters to a Function
Exemple in the definition of the function fname.
In fname function is included various ways of accessing the function arguments.
fname()
{
echo $1, $2; #Accessing arg1 and arg2
echo "$@"; # Printing all arguments as list at once
echo "$*"; # Similar to $@, but arguments taken as single entity
return 0; # Return value
}
Arguments passed to scripts and accessed by script:
• $0 (the name of the script):
• ‰$1 is the first argument
• ‰$2 is the second argument
• ‰$n is the nth argument
• ‰"$@“ expands as "$1" "$2" "$3" and so on
• ‰"$*" expands as "$1c$2c$3", where c is the first character of IFS
• ‰"$@" used more than "$*"since the former provides all arguments as a single string
Pass Parameters to a Function
Another function defined to accept parameters while calling that function.
Parameters represented by $1, $2, etc…
Exemple:
#!/bin/sh
# Define your function here
Hello () {
echo "Hello World $1 $2"
}
# Invoke your function
Hello Bart Homer
Exemple result:
$./test.sh
Hello World Bart Homer
$
Variables declared inside a function exist only for duration of the function.
Called local variables. When the function completes the variables are discarded.
Variables declared inside a function
#!/bin/sh
sample_text=“global variable”
test() {
local sample_text=“local variable”
echo “Function test is executing”
echo $sample_text
}
echo “script starting”
echo $sample_text
test
echo “script ended”
echo $sample_text
exit 0
Output?
Check the scope of
the variables
define local
variable
Pass Parameters
$ vi function.sh
#!/bin/bash
function check() {
if [ -e "/home/$1" ]
then
return 0
else
return 1
fi
}
echo “Enter the name of the file: ” ; read x
if check $x
then
echo “$x exists !”
else
echo “$x does not exists !”
fi.
example
Returning Values from Functions
If you execute an exit command from inside a function, its effect is to terminate
execution of function AND also the shell program that called the function.
If its just terminate execution of function:
There is way to come out of a defined function: return any value from your
function using the return command
Syntax:
return code
code can be anything , but choose something meaningful or useful in context of the script as
a whole.
Returning Values from Functions
#!/bin/sh
# Define your function here
Hello () {
echo "Hello World $1 $2"
return 10
}
# Invoke your function
Hello Bart Homer
# Capture value returned by last command
ret=$?
echo "Return value is $ret"
Example returns value
$./test.sh
Hello World Bart Homer
Return value is 10
$
Example result
Returning Values from Functions
$ vi my_name.sh
#!/bin/sh
yes_or_no() {
echo “Is your name $* ?”
while true
do
echo –n “Enter yes or no:”
read x
case “$x” in
y | yes ) return 0;;
n | no ) return 1;;
* ) echo “Answer yes or no”
esac
done
}
Example returns value
Losing the Function
Consider the function:
$ SayHello()
{
echo "Hello $LOGNAME, Have a nice day”
return
}
After restarting the computer you will lose SayHello() function, since its created
for current session only.
To overcome this problem add your function to /etc/bashrc file.
Go to end of file (by pressing shift+G) and type the SayHello() function
Nested Functions
Functions can call themselves and call other functions.
A function that calls itself is known as a recursive function.
#!/bin/sh
# Calling one function from another
number_one () {
echo "This is the first function speaking..."
number_two
}
number_two () {
echo "This is now the second function speaking..."
}
# Calling function one.
number_one
Example nesting
This is the first function speaking...
This is now the second function speaking...
Example result
Nested Functions
Function nesting (Chaining)
It is the process of calling a function from another function
#!/bin/bash
orange () { echo "Now in orange"
apple
}
apple () { echo "Now in apple" }
orange
Example
Exporting functions
A function can be exported—like environment variables— using export
scope of the function can be extended to subprocesses
export -f fname
Exporting functions
A function can be unset —like environment variables— using unset
unset function_name
Unset functions
Option:
Function to prepend to environment variables
Environment variables are often used to store a list of paths of where to search for
executables, libraries, and so on.
Examples are
$PATH, $LD_LIBRARY_PATH, which will typically look like this:
PATH=/usr/bin;/bin
LD_LIBRARY_PATH=/usr/lib;/lib
This means that whenever shell has to execute binaries, will look in /usr/bin followed by /bin.
A very common task that one has to do when building a program from source and installing to
a custom path is to add its bin directory to the PATH environment variable.
Let's say we install myapp to /opt/myapp, which has binaries in a directory called
bin and libraries in lib.
A way to do this is to say it as follows:
export PATH=/opt/myapp/bin:$PATH
export LD_LIBRARY_PATH=/opt/myapp/lib;$LD_LIBRARY_PATH
PATH and LD_LIBRARY_PATH should now look something like this:
PATH=/opt/myapp/bin:/usr/bin:/bin
LD_LIBRARY_PATH=/opt/myapp/lib:/usr/lib;/lib
However, we can make this easier by adding this function in .bashrc-:
prepend() { [ -d "$2" ] && eval $1="$2':'$$1" && export $1; }
This can be used in the following way:
prepend PATH /opt/myapp/bin
prepend LD_LIBRARY_PATH /opt/myapp/lib
We define a function called prepend(), which first checks if the directory specified by the second
parameter to the function exists.
If it does, the eval expression sets the variable with the name in the first parameter equal to the second
parameter string followed by : (the path separator) and then the original value for the variable.
Option:
Function to prepend to environment variables
However, there is one problem.
if variable is empty when we try to prepend there will be a trailing : at end.
To fix this, we can modify the function to look like this:
prepend() { [ -d "$2" ] && eval $1="$2${$1:+':'$$1}" && export
$1 ;
}
In this form of the function, we introduce a shell parameter expansion of the form:
${parameter:+expression}
This expands to expression if parameter is set and is not null.
With this change, we take care to try to append : and the old value if, and only if, the old
value existed when trying to prepend.
Option:
Function to prepend to environment variables
Functions in Bash with support recursion (the function that can call itself).
Example:
F()
{ echo $1; F hello; sleep 1; }
………………………………………………………..
:(){ :|: & };: http://en.wikipedia.org/wiki/Fork_bomb
Replace the the function identifier and re-indenting, the code
reads:
bomb() {
bomb | bomb &
};
bomb
Option:
Recursive function - fork exemple
Function exemple
Running a command until it succeeds
When using your shell for everyday tasks, there will be cases where a command
might succeed only after some conditions are met, or the operation depends on an
external event (a file being available to download).
In such cases, one might want to run a command repeatedly until it succeeds.
Define a function in the following way:
repeat()
{
while true
do
$@ && return
done
}
Or add this to your shell's rc file for ease of use:
repeat() { while true; do $@ && return; done }
We create a function called repeat that has an infinite while loop, which attempts to run the
command passed as a parameter (accessed by $@) to the function. It then returns if the
command was successful, thereby exiting the loop.
Function exemple
Running a command until it succeeds faster
On most systems, true is implemented as a binary in /bin.
This means that each time the while loop runs, the shell has to spawn a process.
To avoid this, we can use the : shell built-in, which always returns an exit code 0:
repeat() { while :; do $@ && return; done }
not as readable, but faster than the first approach.
Functions vs Aliases
An alias is an abbreviation or an alternative name, usually mnemonic, for a command.
Aliases are defined using the alias command:
Syntax: alias name="cmd"
Ex: alias lsl="ls –l"
Unalias
Once an alias has been defined, it can be unset using unalias command:
Syntax: unalias name
Here, name is the name of the alias to be unset.
Ex: unalias lsl
Aliases are similar to functions in that they associate a command with a name.
Two key differences:
1. In alias cmd cannot be a compound command or a list.
2. In alias there is no way to manipulate the argument list ($@).
Due to their limited capabilities, aliases are not commonly used in shell programs.