Bash Structure

Basic layout and structure of a Bash Shell Script

Structure of a Bash Shell Script


So far we have seen some very simple examples of the Bash shell in use, however, the real power of Bash is unleashed when it used for scripting. Bash can automate procedures, functions, process files, act interactively with users plus much more. When you start writing your first scripts, it is important to get into a good habit of clearly labelling each section of the script with comments and any details that are relevant. Always use comments, add the authors name to the script, maybe include a version number of a script. You should also add a brief description as to the main function of the script. Always try to make the script easy to read so that others can easily understand the functionality of the script. Below are a few hints and tips that you should try to adopt within your scripts.



Purpose of the script


One of the most important aspects when creating a script is to work out exactly what you want your script to do. Although this may sound obvious, a little planning before hand can often make for a better script. Where possible, break your script down into sections that handle specific functions. Remember to comment these clearly. It is always good practice to indicate how this script is run. Is it run manually or is it scheduled via crontab or by another scheduler? What permissions need to be set, can anyone run this? Also if possible, list any files that are associated with this script.


Basic layout of a script


The first line of any script should be a line that specifies what interpreter is to be used for this script. This line is commonly known as "hash bang" or "shebang". The first two characters of this line are #! followed by the path of an interpreter to use. In our examples we will be using the following #!/bin/bash.



#!/bin/bash

Comments


Comments can be made on any line of a script. Comments can even be on the same line as a command as long as they follow the command in question. Comments are easily identified as they always begin with the hash character #. The only exception to this rule is the very first line.



#!/bin/bash
#
# Script Name: mytest.sh
#
# Author: Name of creator
# Date : Date of creation
#
# Description: The following script reads in a text file called /path/to/file 
#              and creates a new file called /path/to/newfile
#
# Run Information: This script is run automatically every Monday of every week at 20:00hrs from
#                  a crontab entry. 
#
# Error Log: Any errors or output associated with the script can be found in /path/to/logfile
#

By providing simple comments, we can quickly get an overview of the functionality of the script. A quick word of warning! Although having comments in the header is good practice, you must not take the comments alone. On many occasions I have come across scripts that have been copied and then modified with no change to the header comments. Sometimes the comments may not be a true reflection of the code beneath!


Handling Errors


It is always good practice to redirect any errors from your script to an error file such as "error.log". This way, a record of any errors that were encountered at run time can be investigated. A simple way to handle error log files is to use some standard redirection commands. Below is an example of stdout going to a file called output.log and any errors will go to a file called errors.log. For more information on standard redirection see our section on Output Redirection Pipes and Redirection



#!/bin/bash
#
# Script Name: /home/john/scripts/mytest.sh
#
# Author: John
# Date : 21.10.2013
#
# Description: The following script defines a variable called test that holds today's date.
#
# Run Information: This script is run manually.
#
# Standard Output: Any output is sent to a file called /home/john/scripts/output.log
#
# Error Log: Any errors associated with this script are sent to a file called /home/john/scripts/errors.log
#

exec 1>>/home/john/scripts/output.log
exec 2>>/home/john/scripts/errors.log

test=$(date) 
echo "Today's date is $test"

Filename and Permissions


When you create a script, you should always try to give it a meaningful name. Ideally a name that reflects the purpose of the script. Or at least adopt a naming convention that can be used across your enterprise. You may want a prefix to identify if the jobs is "Production" or "Test". Normally you will give your filename an extension of ".sh". This signifies that this is a shell script.

By default, when you create a file under Linux, the permissions will be set so that you are unable to execute the contents of the file. To make a file executable, you will need to modify its permissions using the "chmod" command as follows:

Initially the file is created with the permissions "rw-rw-r--" meaning that the file permissions are read/write for the owner and group and read only for other users.



john@john-desktop:~/scripts$ ls -l test.sh
-rw-rw-r-- 1 john john 14 Oct 21 12:29 test.sh

To make this file executable, we issue the "chmod" command as follows:



john@john-desktop:~/scripts$ chmod +x test.sh
john@john-desktop:~/scripts$ ls -l test.sh
-rwxrwxr-x 1 john john 14 Oct 21 12:29 test.sh

After issuing the "chmod" command, we can see that the file is now executable by all users! For more information on file permissions, see our section on File Permissions.



Executing Scripts and your $PATH


Once you have created a script and assigned the relevant permissions, you will need to execute the script. To execute a script we simply can type the full name of the script that we wish to run. However, unless the full path of the script is specified or the actual path to the script is found within our $PATH variable, then our script will fail. Below I will execute a script called "test.sh". This will fail initially, however, if we supply the absolute path to the script, we can then run this:



john@john-desktop:~/scripts$ pwd
/home/john/scripts

john@john-desktop:~/scripts$ ls -l test.sh 
-rwxrwxr-x 1 john john 40 Oct 21 20:17 test.sh

john@john-desktop:~/scripts$ test.sh
test.sh: command not found

john@john-desktop:~/scripts$ ./test.sh 
This is a test...

john@john-desktop:~/scripts$ source test.sh
This is a test...

john@john-desktop:~/scripts$ /home/john/scripts/test.sh
This is a test...

In the above example our test script is located in the directory "/home/john/scripts". We confirmed this by using the "pwd" command to display our current working directory and we used the "ls -l" command to display our file and its permissions within this location.

Notice when we tried to run the script by simply typing its name we received an error. This is because the script could not be found within any directories specified within our $PATH variable. When a program is run, the paths within the variable $PATH are searched for the presence of our program/command. Below is a listing of the paths that are defined to my $PATH variable. We can display the contents of the $PATH variable by issuing the command "echo $PATH"



john@john-desktop:~/scripts$ echo $PATH 
/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

As we can see from the above, the location of our script "/home/john/scripts" is not in this searchable path. Therefore we need to either add this path or indicate where the program can be found. One of the most common ways of executing a script is to prefix the script name with the characters "./" These characters literally mean run from the current location (current working directory). You can also use the source command to achieve the same result. Alternatively, we can provide the full path to the location of the script "/home/john/scripts/test.sh".

If you have many scripts that you want to run from a specific location, it is probably best to add the path of this script directory to your $PATH variable. This can be done by simply issuing the following command "PATH=$PATH:/home/john/scripts":



john@john-desktop:~/scripts$ echo $PATH
/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

john@john-desktop:~/scripts$ PATH=$PATH:/home/john/scripts
john@john-desktop:~/scripts$ echo $PATH
/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/john/scripts

john@john-desktop:~/scripts$ test.sh
This is a test...

Although this is useful, the change to the $PATH variable is lost when the current terminal is closed. A more permanent solution would be to add our additional path into our ".bashrc" file. This file is located in the "/home/user_id" area.



john@john-desktop:~$ pwd
/home/john
john@john-desktop:~$ ls -al .bashrc 
-rw-r--r-- 1 john john 3384 Oct 12  2012 .bashrc

Now we use the editor of our choice and add the following entry to this file:



# My Custom PATH entries 
PATH=$PATH:/home/john/scripts

Now when I open a new terminal and issue the "echo $PATH" command, I can now see my added path. The script "test.sh" can now be run. It is also worth remembering that the paths are searched from left to right. If you have a script by the same name that is located in one of the path entries before our script area, then that will be executed!



john@john-desktop:~$ echo $PATH
/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/john/scripts
john@john-desktop:~$ test.sh
This is a test...