Bash Tutorial

                                                                    

In this tutorial, you will learn about the Bourne Again or bash shell and how to use it. This tutorial is based upon a CS2351 lab assignment from Oklahoma State University.  Shell scripts to be run in the Bourne Again shell will be developed and shell control statements used in the Bourne Again shell will be introduced. Control statements covered include: test, for, for in, if-then-else-fi, if-then-elif-fi, while, until, and case. Relational operators and expr used in the Bourne Again shell are introduced. Errors you may encounter are discussed.  It is recommended that you complete this tutorial using the LittleFe cluster computer at littlefe.nwmissouri.edu or with one of the csgrad servers .

The Bourne Again shell was written in 1989 by Brian Fox and extends upon many of the features of the Bourne shell. It has many similar features to the features of the Korn shell or the C shell.  These features include a command history that may be accessed by pressing the up arrow to scroll through past commands, an autocomplete feature that will complete file or command names by pressing the tab key, or the command line editing like in the Korn shell. Aliases are also available.  As the Bourne Again shell is You will not be able to use them while you are in the Bourne shell. This may be frustrating to you. One reason why you are looking at this shell, is that it is the main shell on many UNIX machines.

You can change to another shell on your machine at any time by just typing in the name of that shell at the prompt. Type in:

                sh

What happened when you did this? ____________________________________________________

Your prompt should have changed. It should have something similar to # > or sh-3.2$. Usually it would be a $ sign but you changed from the bash shell to the Bourne shell. To return to the bash shell, type:

ctrl - d
   or
exit

You will then be returned to the bash shell prompt. Note that you must be in the bash shell to log off.

First, let's make some script files in the home directory for the chmod command for you to use when you are in the Bourne Again shell.  The chmod command allows you to change the permissions of a file. Note that there are three different levels of permissions - user, group, and world.  With in each level, you may grant read, write, and/or execute permissions.  Change to the home directory. Type in:

                 cd ~

Next, type in:

                 ls -al

What files did you see listed? ____________________________________________________________

All of these shell scripts will work in the Bourne Again shell because the start with the header #!/bin/bash. This is used to start a bash shell script.  You are going to create a script to change the permissions of a file. First open nano type the following contents into the text editor and save the file with the name chb.

#!/bin/bash
# Change mode command
# This file will make a file executable
echo "Read in name of file to be made executable: "
read filename
chmod 700 $filename
echo "$filename is now executable: "
ls -l $filename

Make this file executable by typing chmod 700 chb.

Make another file called lsl. This file will allow you to print detailed contents of a directory without having to type the full command ls -al. Open nano, and type in the following then save the file as lsl.

#!/bin/bash
# This shell script will print all directory contents including
# permissions and size data.
ls -al

Make this file executable by using the chb file you just created by typing ./chb and recall that the first line starts with #!/bin/bashbash stands for the Bourne Again shell. Note that the Bourne Again shell uses the echo statement to print data to the console. You will notice that many functions of this shell allow for programming similar to languages that you know like C, Python, Java, and Perl.

Shell scripts that you write in the Bourne Again shell should work automatically in other shells such as the Korn shell. However, this does not necessarily work in the reverse order. If a program was written in the Korn shell, it will not necessarily work in the Bourne Again or Bourne shell.

Ensure that you are in your home directory by typing cd ~

Now create a new directory by typing mkdir bashLab and change to that directory by typing cd bashLab  You will create several files in this directory.  Next, type the following command:

                 pwd

What was the output?______________________________________________________________

Flow of Control Structures

You will now be introduced to control structures available in bash. Some will look similar to those used in other programming languages such as Java. These are similar to if-then-else type of statements used in other programming languages.

Test Command

The first control statement is the test command. The test command evaluates an expression and returns a condition code indicating the expression is either true (= 0) or false (not = 0).

This command is used for branching. The if statement has the form:

if (test command)
then
    commands
fi

The test command has many options that available including:

-f tests to see if filename listed is a file
-d tests to see if filename listed is a directory
-r tests to see if filename is a readable file
-w tests to see if filename is writeable
-x tests to see if filename is executable

Create a new shell script called sample1. Enter the following exactly as shown.

#!/bin/bash
if (test -f books)
then
    ls
fi

The "then" must be on the second line. Notice that the control structure ends in "fi" or "if" spelled backwards. You will work with other bash control structures soon. Most end with the control structure name spelled backwards.

Assuming you are still in the bashLab directory, after you have completed sample1, use the chb shell script that you made earlier. At the prompt, type in:

../chb

You will be asked to "Read in name of file to be made executable". Type in:

sample1

You should then get a reply that "sample1" is now executable including output describing file permissions. The chb shell script can be used for other files that need to be made executable in this lab. Now type in:

./sample1

This should run the sample1 shell script. What answer did you get? _________________________________________________________________________________

Now create a file called books with the following data:

Hadoop Operations, Eric Sammer, 9781449327057
CUDA By Example: An Introduction to General-Purpose GPU Programming, Jason Sanders and Edward Kandrot, 9780131387683
Structured Parallel Programming: Patterns for Efficient Computation, Michael McCool James Reinders and Arch Robinson, 0124159931

Then, run sample1 again.  What happened?

_________________________________________________________________________________

In the case of the single if statement, if the statement is true, the statement after the "then" is carried out. If it is not true, then nothing is done. Is the test condition true? ________________________

The two-branch if-statement is similar to the single-branch if-statement. Similar to the if then-clause, the else-clause must start on a new line. Create another script called sample2 with code exactly as shown below.

#!/bin/bash
if (test -d books)
then
    echo "books is a directory file"
else
    echo "books is a plain file with the "
    echo "following access permissions:"
    ls -l books
fi

Use the chb shell script to make this file executable and then type in:

./sample2

Is the test condition true ?___________________________

What is printed ? ________________________________________________________________________________

For loops

The bash for loop has the following format:

for loop-index
do
    commands
done

The loop-index can automatically take on the value of each command line parameter, one at a time. It can then perform a series of commands involving each parameter.

Use the interactive cat command (cat > <filename>) to create a file called wordsInBooks that contains the following shell script. When you use the cat command, and you are done entering information, press ctrl - c.  Be sure to press enter after entering the last line.

#!/bin/bash
for i
do
    grep -n $i books
done

Make this file executable by using chb.  

The grep command is a search command that allows you to search for word or character matches in files. The shell script above will look for the list of arguments in the file books that are listed after the name of the shell script. In the example below, the wordsInBooks is the name of the shell script and Michael, Arch, and hal are arguments. You will search for these patterns in the file books.

Now, issue the command:

./wordsInBooks Michael Arch hal

What was the result? ________________________________________________________________

________________________________________________________________________________

________________________________________________________________________________

Next, issue the command:

./wordsInBooks to for

The grep command performs a global search to find matching patterns. It searches through each line in a file to find matches. This command can be time consuming if you have a large file. What was the result of the previous search? ______________________________________________________________________

_______________________________________________________________________________

Now try the search:

./wordsInBooks \&

This command searches for an ampersand.  The backslash is required because & is a special character which when appended to a command, indicates that the command is to run as a background process thereby leaving the terminal free to issue other commands. Here we wanted to search for the &, hence the backslash. What was the result? _______________________________________________________________________________

For In

The for in control structure has the following format:

for loop-index in argument-list
do
    <command list>
done

This structure assigns the value of the first argument in the argument list to the loop-index and executes the commands between the do and done statements. The do and done statements mark the beginning and end of the for loop. To see how this structure works, create the following script named names:

#!/bin/bash
for file in books*
do
    echo "==============================="
    echo "Sorted contents of $file"
    echo "==============================="
    sort -b $file
    echo
done

Create three copies of the file books by issuing the following commands.

cp books books1
cp books books2
cp books books3

Add different books to each of the files above.

Use the shell script chb to make names executable. Then execute names as follows.

./names | more

*

Describe the output.

________________________________________________________________________________

________________________________________________________________________________

________________________________________________________________________________

Relational Operators used in the Bourne shell

Summary of Relational Operators ( see http://www.tldp.org/LDP/abs/html/comparison-ops.html )

Operator Used to test:
= equality of two strings
!= inequality of two strings
-ne inequality of two integers
-lt true if integer on left is less than integer on right
-le true if integer on left is less than or equal to integer on right
-gt true if integer on left is greater than integer on right
-ge true if integer on left is greater than or equal to integer on right
-f true if a file is a plain file
-d true if a file is a directory
-a logical and - true if expresssion on left and right are both true
-o logical or - true if expresssion on left or right is true

If.. then .. else.. fi

The if-then-else-fi clause can be used to cause execution of statements to occur if the specified test proves false. The general format of the if-then-else-fi is:

if condition
then
    commands
else
    commands
fi

If the result of the condition is true, then the commands enclosed between the then and else are executed. Otherwise, the commands between the else and fi will be executed.

If .. then .. elif .. else ..fi

The elif statement combines the else and if statements and allows you to construct a nested set of if-then-elif-else-fi structures. The format for this is:

if condition
then
    commands
elif
then
    commands
else
    commands
fi

As with else-if statements in Java, you may use as many elifs as you want. After the last elif, it is possible to use an else so a set of commands can be executed if none of the preceding expressions are true. Enter the following program called classification.

#!/bin/bash
#
# This program was written by: ( type your name here)
# This program will allow user to read in the number
# of hours a student has completed.
# It will then determine what classification the
# student is based on the hours completed.
#
#
echo "Enter the number of hours the student has completed: "
read number
if [ $number -ge 1 -a $number -le 27 ]
then
    echo Class = Freshman
elif [ $number -gt 27 -a $number -le 60 ]
then
    echo Class = Sophomore
elif [ $number -gt 60 -a $number -le 93 ]
then
    echo Class = Junior
else
    echo Class = Senior
fi

Be sure to make the program executable using chb

Notice that you may enter your name in the comments section program.

Note: when you enter this program, be sure to leave a space after the open bracket ([) and before the close bracket (]). If you do not leave a space between the brackets and other characters, you will get an error message. Also note that the if statement ends with the fi statement. Run the program as follows:

./class2

At the prompt, enter a number. Execute the program several times using different numbers that fit in the different categories to test your shell control program.

While

In the while structure, as long as the test-command returns a true exit status, the structure executes the series of commands between do and done. Before each pass through the commands, the test-command is evaluated. When the exit status of the test-command is false, control is passed to the statement immediately following the done statement. The while construct appears as follows.

while test-command
do
    commands
done

Create the following script called looping.

#!/bin/bash
count=1
while [ $count -le 10 ]
do
    echo "$count"
    count=`expr $count + 1`
done

Note: you need back quotes when you use the expr command in this example.

Describe the output of this script. _______________________________________________________________________________

Arithmetic operators - Expr command

Notice that the expr command was used in the while structure. It evaluates an expression. It evaluates character strings that represent either numeric or nonnumeric values. The expr command returns an exit status of 0 (zero) if the expression is neither a null string nor the number 0; it returns a status of 1 if the expression is null or 0; and it returns a status of 2 if the expression is invalid.

The following examples show commands that call expr to evaluate constants. You can also use expr to evaluate variables in a shell script. Type in the following examples and fill in the answers next to the expression:

expr 3 + 5 ___________
expr 3 "*" 9 ___________
expr 8+2 (no spaces between the 1 & 2) ___________
expr "9 * 14" ___________
count=103 ___________
expr $count + 9 ___________

Expr writes its results to the standard output. By using the backquote mechanism, you can assign expr's results to a shell variable:

result=`expr 1 + 2`
echo $result

These are just a few examples of how expr works. You can try other options or read more about it online.

Until

The until and while structures are very similar. They differ only in the evaluation of the test. The until continues to loop until the test-command returns a true exit status. The while structure loops while the test-command continues to return a true or nonerror condition. The until structure is shown below:

until test-command
do
    commands
done

Create the following script. Call it numbers.

#!/bin/bash
read number
until [ $number -lt 0 ]
do
    echo $number
    number=`expr $number - 1`
done

Make this script executable and run it. You will see a blank line.  Type in  8 as the input value. Describe the output of this script.
_______________________________________________________________________________
_______________________________________________________________________________

Case

The case structure provides a multiple branch decision mechanism. The case statement is similar to the switch statement in Java. The control path depends on a match between the test-string and one of the patterns.  An example of this mechanism follows below.

case test-string in
    pattern-1)
        command list;;
    pattern-2)
        command list;;
    pattern-N)
        command list;;
esac

Note that the end of the case statement is esac.

Type in the following example using the nano editor. Name it choose:

#!/bin/bash
# This program was written by: (your name here)
echo "Enter 1, 2, 3, or 4: "
read number
case $number in
    1)
        date;;
    2)
        ls;;
    3)
        pwd;;
    4)
        who | wc -l;;
    *)
        echo You have entered an incorrect value;;
esac

After you have completed this script, make it executable and try it.

Error resolution in bash

If you are using an "if" statement in bash, you could get an error such as this:

    <name of file>: syntax error at line7: `elif' unexpected

This likely means that you put the then statement on the same line with the "if" statement. You need to have the "then" statement on the next line as the first word on that line.

Another problem may be if you put a # (pound sign) in the first line without #!/bin/bash. UNIX will assume that you want to run the program in the default login shell, which may not be bash.  If your defualt shell is not bash, you may get an error when the script is executed. An example would be if you used the # sign in the first line of the program, choose above, you may get an error message:

        read: Command not found.
        number: Undefined variable.

Other errors occur when you add a space between the "=" (equal) sign when you are assigning values to variables. You must not leave spaces on either side of an equal sign for assignment statements in bash.

In order to get log off the machine or close your terminal window, type in:

exit