Bash Scripting: Functions

This month I’ve decided to make a return to my Bash Scripting series. I’ve recently discovered and made use of a new item in scripting bash, called “functions”. So… what are functions? Say you’ve written a script in bash. You have plenty of logging to a log file, but each log file entry is also mirrored out to the command line (helpful for running the script manually). To do this normally, you’d need to use two commands per output. E.g.

echo "script starting..." >> $logfile
echo "script starting..."

Say you also want to use want to use timestamps, you’d then need three lines. E.g.

currentDate=`date`
echo $currentDate "script starting..." >> $logfile
echo $currentDate "script starting..."

Now to use this throughout your script, you’ll be adding three lines per line of message, also introducing typos and mistakes that could either give incorrect information, conflicting information or even make the script error out.
What can I do?
Use a function. Functions can load a set series of tasks and set them to be triggered by a word, in much the same way as loading a variable. The function needs to be set before it’s used in the script but otherwise acts the same as any other Bash command like ‘cp’ or ‘rm’. The format of the function is as follows:

function [name] {
[command]
}

Lets go through this section-by-section:

  •  “function” – This should be used as is and informs the script that you are loading a function. Leave a space after this.
  • “[name]” – This is the name you want to store the function under and the same name you will use to call it back. This should be created with the same rules as a variable (e.g. no special characters, no spaces). There should be a space after this.
  • “{“ – This is the open bracket to denote the start of the commands to run when the variable is called. There doesn’t need to be a return after this, but it’s better practice to do so. At minimum, there should be a space following.
  • “[command]” – This is where you’d add your commands you want the function to run. Each new command should be on a new line.
  •  “}” – This is the close bracket to denote the end of the commands to run when the variable is called.

Okay, I think I get it… Don’t worry; it makes more sense if we return to our issue. What we should do is add the repeated items into a function, then call this as required. Let’s create that function now:

function write_to_Log_and_screen {
		currentDate=`date`
		echo $currentDate >> $logfile
		echo $currentDate
}

Now I’d call this in a script as follows:

write_to_Log_and_screen

This would result in the current date being added to both the log file and the terminal output, replacing 3 lines of code with one in each area you use it.

Amsys - Darren:~ darrenwallace$ /Users/darrenwallace/Desktop/function_example.sh
Wed 12 Feb 2014 12:45:20 GMT
Amsys - Darren:~ darrenwallace$

But how do I add more text? OK, so the above doesn’t quite replace the functionality, as we’re not saving any real information to the log or terminal. To do that, we need to make a change to our function, and how we use it. By default, the “$1” variable is the first variable password to a command. We need to add this variable to the function as follows:

function write_to_Log_and_screen {
		currentDate=`date`
		echo $currentDate $1 >> $logfile
		echo $currentDate $1
}

Now we have the function configured, we need to modify how we use it. Call the function as before, but enter whatever text you want in the log in speech marks following the function call. E.g.

write_to_Log_and_screen "This is a test"

Which should produce the following:

Amsys - Darren:~ darrenwallace$ /Users/darrenwallace/Desktop/function_test.sh
Wed 12 Feb 2014 14:04:58 GMT This is a test
Amsys - Darren:~ darrenwallace$

And there you have it, a working function to write to the log and the terminal window during scripts. Example Now I’ll show you two scripts. Both are identical, except the first writes the log entries manually, and the second uses the function command to show the different lengths of scripts. Example 1: No function.

#!/bin/bash
# Logfile varible
logfile="/Users/$USER/Desktop/test.log"
	currentDate=`date`
	echo $currentDate "test line 1" >> $logfile
	echo $currentDate "test line 1"
	currentDate=`date`
	echo $currentDate "test line 2" >> $logfile
	echo $currentDate "test line 2"
	currentDate=`date`
	echo $currentDate "test line 3" >> $logfile
	echo $currentDate "test line 3"
	currentDate=`date`
	echo $currentDate "test line 4" >> $logfile
	echo $currentDate "test line 4"
	currentDate=`date`
	echo $currentDate "test line 5" >> $logfile
	echo $currentDate "test line 5"
	currentDate=`date`
	echo $currentDate "test line 6" >> $logfile
	echo $currentDate "test line 6"
	currentDate=`date`
	echo $currentDate "test line 7" >> $logfile
	echo $currentDate "test line 7"
	currentDate=`date`
	echo $currentDate "test line 8" >> $logfile
	echo $currentDate "test line 8"
	currentDate=`date`
	echo $currentDate "test line 9" >> $logfile
	echo $currentDate "test line 9"
	currentDate=`date`
	echo $currentDate "test line 10" >> $logfile
	echo $currentDate "test line 10"
exit 0

Example 2: With a function.

#!/bin/bash
# Logfile varible
logfile="/Users/$USER/Desktop/test.log"
# Function to write to the logfile, but will also output the same information to the terminal window.
function write_to_Log {
	currentDate=`date`
		echo $currentDate $1 >> $logfile
		echo $currentDate $1
}
	write_to_Log "test line 1"
	write_to_Log "test line 2"
	write_to_Log "test line 3"
	write_to_Log "test line 4"
	write_to_Log "test line 5"
	write_to_Log "test line 6"
	write_to_Log "test line 7"
	write_to_Log "test line 8"
	write_to_Log "test line 9"
	write_to_Log "test line 10"
exit 0

Using this example, we have reduced the number of lines by 13 and reduced the complexity when reading the script at the same time. Summary And that’s it for this month, hopefully this overview of function and examples of its use will give you other ideas on using it in your scripts. Feel free to share your examples in the comments below.