Beginners Guide To BASH - Conditions And Variables

Bash Tutorial rssget
Bash Tutorial rssget.

Introduction

Welcome to the third part of the "Beginners Guide To BASH". If you have missed the previous two articles then you will probably want to know what makes this guide different to other BASH scripting guides.

This guide is being written by a complete novice to BASH and so as a reader you learn as I learn. Whilst I am a novice to BASH I do come from a software development background although most of the stuff that I have written has been for the Windows platform.

You can see the first two guides by visiting:

If you are new to BASH scripting I recommend reading the first two guides before continuing with this one.

In this guide I will be highlighting how to use conditional statements to test user input and to control how a script functions.

Install rsstail

In order to follow this guide you will need to install a command line application called rsstail which is used to read RSS feeds.

If you are using a Debian/Ubuntu/Mint based distribution type the following:

sudo apt-get install rsstail

For Fedora/CentOS etc type the following:

yum install rsstail

For openSUSE type the following:

zypper install rsstail

The IF statement

Open up a terminal and create a file called rssget.sh by typing the following:

sudo nano rssget.sh

Within the nano editor enter the following text:

#!/bin/bash
rsstail -u http://z.about.com/6/o/m/linux_p2.xml;

Save the file by pressing CTRL and O and then exit by pressing CTRL and X.

Run the script by typing the following:

sh rssget.sh

The script will return a list of titles from the linux.about.com RSS feed.

It isn't an overly useful script because it just retrieves the titles from one RSS feed but it does save having to remember the path to the Linux.about.com RSS feed.

Open up the rssget.sh script in nano again and edit the file to look as follows:

#!/bin/bash

if [ $1 = "verbose" ]
then
    rsstail -d -l -u http://z.about.com/6/o/m/linux_p2.xml;
fi

Run the script again by typing the following:

sh rssget.sh verbose

This time the RSS feed comes back with the title, link and description.

Let's analyse the script in a bit of detail:

The #!/bin/bash appears in every script we write. The next line basically looks at the first input parameter provided by the user and compares it to the word "verbose". If the input parameter and the word "verbose" matches the lines between then and fi are ran. 

The above script is obviously flawed. What happens if you don't provide an input parameter at all? The answer is you get an error along the lines of unexpected operator.

The other major flaw is that if you don't provide the word "verbose" then nothing happens at all. Ideally if you don't provide the word verbose the script would return a list of titles.

Use nano again to edit the rssget.sh file and amend the code as follows:

#!/bin/bash

if [ $1 = "verbose" ]
then
    rsstail -d -l -u http://z.about.com/6/o/m/linux_p2.xml;
else
    rsstail -u http://z.about.com/6/o/m/linux_p2.xml;
fi

Save the file and run it by typing the following:

sh rssget.sh verbose

A list of titles, descriptions and links will appear. Now run it again as follows:

sh rssget.sh titles

This time just a list of titles appear.

The extra part of the script is on line 4 and introduces the else statement. Basically the script now says if the first parameter is the word "verbose" get the description, links and titles for the RSS feed but if the first parameter is anything else just get a list of titles. 

The script has improved slightly but is still flawed. If you fail to enter a parameter you will still get an error. Even if you do provide a parameter, just by saying you don't want verbose doesn't mean you want titles only.

You may have just spelt verbose wrong for instance or you may have typed pigeons which is of course meaningless.

Before we try and clear these issues I want to show you one more command that goes with the IF statement.

Edit your rssget.sh script to look as follows:

#!/bin/bash

if [ $1 = "all" ]
then
    rsstail -d -l -u http://z.about.com/6/o/m/linux_p2.xml;
elif [ $1 = "description" ]
then
    rsstail -d -u http://z.about.com/6/o/m/linux_p2.xml;

else
    rsstail -u http://z.about.com/6/o/m/linux_p2.xml;
fi

I decided to get rid of the word verbose and replaced it with all. That isn't the important part. The script above introduces elif which is a short way of saying ELSE IF. 

Now the script works as follows. If you run sh rssget.sh all then you get descriptions, links and titles. If instead you just run sh rssget.sh description you will just get titles and descriptions. If you supply any other word you will get a list of titles.

This introduces a way of quickly coming up with a list of conditional statements. An alternate way of doing ELIF is to use what is known as nested IF statements.

The following is an example showing how nested IF statements work:

#!/bin/bash

if [ $2 = "aboutdotcom" ] 
then
    if [ $1 = "all" ]
    then
        rsstail -d -l -u http://z.about.com/6/o/m/linux_p2.xml;
    elif [ $1 = "description" ]
    then
        rsstail -d -u http://z.about.com/6/o/m/linux_p2.xml;

    else
        rsstail -u http://z.about.com/6/o/m/linux_p2.xml;
    fi
else
    if [ $1 = "all" ]
    then
        rsstail -d -l -u http://lxer.com/module/newswire/headlines.rss
    elif [ $1 = "description" ]
    then
        rsstail -d -u http://lxer.com/module/newswire/headlines.rss
    else
        rsstail -u http://z.about.com/6/o/m/linux_p2.xml;
    fi
fi

Feel free to type all that in if you like or copy and paste it into your rssget.sh file. 

The above script introduces a 2nd parameter which lets you choose either "about.com" or "lxer.com" an an RSS feed.

To run it you type in the following:

sh rssget.sh all aboutdotcom

or

sh rssget.sh all lxer

You can of course replace all with descriptions or titles to provide just descriptions or just titles.

Basically the code above says if the second parameter is aboutdotcom then look at the second if statement which is the same one from the previous script else if the second parameter is lxer then look at the inner if statement again to decide whether to show titles, descriptions or everything.

That script is provided purely as an example of a nested IF statement and there are so many things wrong with that script it would take another article to explain them all. The main issue is that it isn't scalable. 

Imagine you wanted to add another RSS feed such as Everyday Linux User or Linux Today? The script would become huge and if you decided that you wanted the inner IF statement to change you would have to change it in multiple places.

Whilst there is a time and place for a nested IF they should be used sparingly. There is usually a way to refactor your code so that you don't need the nested IF at all. I will come on to this subject in a future article.

Let's now look at fixing the issue of people entering duff parameters. For instance in the script above if the user enters something other than "aboutdotcom" as the 2nd parameter then a list of articles appear from the RSS feed from LXER regardless as to whether the user enter lxer or not.

In addition if the user doesn't enter "all" or "description" as the 1st parameter then the default is a list of titles which may or not be what the user intended.

Look at the following script (or copy and paste it into your rssget.sh file.

#!/bin/bash

if [ $2 = "aboutdotcom" ] || [ $2 = "lxer" ] 
then
    if [ $1 = "all" ] || [ $1 = "description" ]  || [ $1 = "title" ] 
    then
        if [ $2 = "aboutdotcom" ] 
        then
    
            if [ $1 = "all" ]
            then
                rsstail -d -l -u http://z.about.com/6/o/m/linux_p2.xml;
            elif [ $1 = "description" ]
            then
                rsstail -d -u http://z.about.com/6/o/m/linux_p2.xml;

            else
                rsstail -u http://z.about.com/6/o/m/linux_p2.xml;
            fi
        else
            if [ $1 = "all" ]
            then
                rsstail -d -l -u http://lxer.com/module/newswire/headlines.rss
            elif [ $1 = "description" ]
            then
                rsstail -d -u http://lxer.com/module/newswire/headlines.rss
            else
                rsstail -u http://z.about.com/6/o/m/linux_p2.xml;
            fi
        fi
    fi
fi

The first thing to note is that the script is now getting fairly big and you can quickly see how out of control nested IF statements can become.

The bit that is important in this script is the IF statement || statement THEN section on line 2 and line 4.

The || stands for OR. So the line if [ $2 = "aboutdotcom" ] || [ $2 = "lxer" ]  checks whether the 2nd parameter is equal to "aboutdotcom" or "lxer". If it isn't then the IF statement is complete because there is no else statement for the outer most IF.

Similarly on line 4 the line if [ $1 = "all" ] || [ $1 = "description" ]  || [ $1 = "title" ] checks whether the 1st parameter is equal to either "all" or "description" or "title". 

Now if the user runs sh rssget.sh potatoes cheese nothing is returned whereas before they would have received a list of titles from LXER.

The opposite of || is &&. The && operator stands for AND.

I am going to make the script look even more like a nightmare but it makes the all important check to make sure the user has provided 2 parameters.

#!/bin/bash

if [ $# -eq 2 ] 
then

    if [ $2 = "aboutdotcom" ] || [ $2 = "lxer" ] 
    then
        if [ $1 = "all" ] || [ $1 = "description" ]  || [ $1 = "title" ] 
        then
            if [ $2 = "aboutdotcom" ] 
            then
    
                if [ $1 = "all" ]
                then
                    rsstail -d -l -u http://z.about.com/6/o/m/linux_p2.xml;
                elif [ $1 = "description" ]
                then
                    rsstail -d -u http://z.about.com/6/o/m/linux_p2.xml;

                else
                    rsstail -u http://z.about.com/6/o/m/linux_p2.xml;
                fi
            else
                if [ $1 = "all" ]
                then
                    rsstail -d -l -u http://lxer.com/module/newswire/headlines.rss
                elif [ $1 = "description" ]
                then
                    rsstail -d -u http://lxer.com/module/newswire/headlines.rss
                else
                    rsstail -u http://z.about.com/6/o/m/linux_p2.xml;
                fi
            fi
        fi
    fi
fi

 

The only bit that is extra in that script is another outer IF statement as follows: if [ $# -eq 2 ]. If you read the article about input parameters you will know that $# returns a count of the number of input parameters. The -eq stands for equals. The IF statement therefore checks that the user entered 2 parameters and if they didn't it just exits without doing anything. (Not particularly friendly).

I am aware that this tutorial is getting quite large. There isn't much more to cover this week but I want to help tidy up the script before we finish.

The one last command that you need to learn about conditional statements is the CASE statement.

#!/bin/bash


if [ $# -eq 2 ]
then
    case $2 in
        aboutdotcom)
            case $1 in
                all)
                    rsstail -d -l -u z.about.com/6/o/m/linux_p2.xml
                    ;;
                description)
                    rsstail -d -u z.about.com/6/o/m/linux_p2.xml
                    ;;
                title)
                    rsstail -u z.about.com/6/o/m/linux.about.com/6/o/m/linux_p2.xml
                    ;;
            esac
            ;;
        lxer)
            case $1 in
                all)
                    rsstail -d -l -u http://lxer.com/module/newswire/headlines.rss
                    ;;
                description)
                    rsstail -d -u http://lxer.com/module/newswire/headlines.rss
                    ;;
                title)
                    rsstail -u http://lxer.com/module/newswire/headlines.rss
                    ;;
            esac
            ;;
        esac
fi

The case statement is a nicer way of writing IF ELSE IF ELSE IF ELSE IF.

For example this logic

IF fruit = bananas 
THEN this
ELSE IF fruit = oranges
THEN this  
ELSE IF fruit = grapes
THEN this
END IF

can be rewritten as:

case fruit in 
    bananas)
                      do this
                      ;;
    oranges)
                      do this
                      ;;
    grapes)
                      do this
                      ;;
esac

Basically the first item after the case is the thing you are going to compare (i.e. fruit). Then each item before the brackets is the thing you are comparing against and if it matches the lines preceding ;; will be ran. A case statement is terminated with the reverse esac (which is case backwards).

In the rssget.sh script the case statement removes some of that awful nesting although not really improving it enough.

To really improve the script I need to introduce you to variables. 

Look at the following code:

#!/bin/bash

lxer="lxer.com/module/newswire/headlines.rss"
aboutdotcom="z.about.com/6/o/m/linux_p2.xml"
display=""
url=""

if [ $# -lt 2 ] || [ $# -gt 2 ]
then
    echo "usage: rssget.sh [all|description|title] [aboutdotcom|lxer]";
    exit;
fi

case $1 in 
    all)
        display="-d -l -u"
        ;;
    description)
        display="-d -u"
        ;;
    title)
        display="-u"
        ;;
esac

case $2 in
    aboutdotcom)
        url=$aboutdotcom;
        ;;
    lxer)
        url=$lxer;
        ;;
esac
rsstail $display $url;

A variable is defined by giving it a name and then assigning a value to it. In the above example the following are variables assignments:

lxer="lxer.com/module/newswire/headlines.rss"
aboutdotcom="z.about.com/6/o/m/linux_p2.xml"
display=""
url=""

The script is instantly more manageable by using variables. For instance each parameter is handled separately and so there are no nested IF statements.

The display variable is now set depending on whether you chose all, description or title and the url variable is set to the value of the aboutdotcom variable or the value of the lxer variable depending whether you chose aboutdotcom or lxer.

The rsstail command now just has to use the value of display and url to run correctly. 

Whilst variables are set just by giving them a name, to actually use them you have to place a $ sign in front of them. In other words variable = value sets variable to a value whereas $variable means give me the contents of the variable.

The following is the final script for this tutorial.

#!/bin/bash

lxer="lxer.com/module/newswire/headlines.rss"
aboutdotcom="z.about.com/6/o/m/linux_p2.xml"
everydaylinuxuser="http://feeds.feedburner.com/everydaylinuxuser/WLlg"
linuxtoday="http://feedproxy.google.com/linuxtoday/linux"
usage="usage: rssget.sh [all|description|title] [lxer|aboutdotcom|everydaylinuxuser|linuxtoday]"
display=""
url=""

if [ $# -lt 2 ] || [ $# -gt 2 ] 
then
    echo $usage;
    exit;
fi

case $1 in 
    all)
        display="-d -l -u"
        ;;
    description)
        display="-d -u"
        ;;
    title)
        display="-u"
        ;;
    *)
        echo $usage;
        exit;
        ;;
esac

case $2 in 
    aboutdotcom)
        url=$aboutdotcom;
        ;;
    lxer)
        url=$lxer;
        ;;
    linuxtoday)
        url=$linuxtoday;
        ;;
    everydaylinuxuser)
        url=$everydaylinuxuser;
        ;;
    *)
        echo $usage;
        exit;
esac

rsstail $display $url;

 

The above script introduces more RSS feeds and there is a usage variable which tells the user how to use the script if they either don't enter 2 variables or they enter incorrect options for the variables.

Summary

This has been an epic article and may have gone too far too soon. In the next guide I will show you all the comparison options for IF statements and there is still much more to talk about with regards to variables.

There is also more that can be done to improve the above script and this will be covered in future guides as we explore loops, grep and regular expressions.

Check out the How To (Scroll down past the categories to see a list of articles) section of linux.about.com to find more useful guides from dual booting Windows and Ubuntu to setting up a virtual machine using GNOME boxes.