CSC 161 Schedule Readings Labs & Projects Homework Deadlines Resources

Bash Shell Scripts

Introduction

The Bash shell allows users to interact with the computer through a terminal window, organizing commands that tie various programs together. First developed by Brian Fox in 1987, the Bash shell seeks to incorporate numerous features from earlier shells for the Unix operating system. In particular, the Bash shell draws upon the Bourne shell (developed by Ken Thompson and then Stephen Bourne around 1977), the Korn shell (developed by David Korn in the early 1980s), and the C shell (developed by Bill Joy, based on Thompson's earlier shells).

Within a terminal window, you already have used several commands within the Bash shell. For example, the Basic Linux Commands reading discussed Bash commands, such as:

In this reading, we consider several additional commands, and we explore how various capabilities can be packaged to accomplish various tasks. The approach is not unlike programming in C (or Scheme), although the programming elements often are entire programs.

A Starting Example

The Bash shell provides many capabilities that are analogous to features of the C programming language (e.g., variables, if-statements, loops), but the Bash shell also allows processing with other programs. The following simple Bash script illustrates some of these possibilities.

#!/bin/bash

#clear the window
clear

#identify the path for the current directory
echo "the path to the current directory is `pwd`"

#print information on C programs,
#   in order of increasing size
echo
echo 'C programs, in ascending order of size:'
ls -l *.c | sort -n -k 5

#finish
echo
echo bash script is done!

When a Bash script is typed into a file, the file must be given "execute" permission before it can be run. Use the chmod utility in the Terminal to set the permissions correctly:

chmod +x bash-script-1

Once the file is appropriately edited and execute permission has been granted, one runs the program in the same way one runs any program:

./bash-script-1

Bash Script Files

A bash script is a file containing bash commands. To make the file executable, we must first take the following two steps.

  1. The script file should contain the following code as its very first line:
    #!/bin/bash
    This specifies which program (bash) should be used to interpret the commands in the script.
  2. The file permissions for the script should be set such that is it executable.
    chmod +x scriptname.sh

The script can then be run like any other executable file:

./scriptname.sh

Actually, we can also run a script as follows (even without making the script executable), but this is less frequently done.

bash scriptname.sh

Script variables

Variables can be created and initialized as shown below. Note that there should NOT be a space between the variable name and the assignment operator. (If the variable name has a space after it, bash will recognize it as a token, but since the variable does not yet exist, bash will assume the token is command name. It will then complain about not finding such a command.)

varname=12

To access the value in a variable, prepend a $ to the variable name. For example,

echo "Value in varname is $varname."

Arithmetic Expressions Require (( ))

The variables in a bash script are by default interpreted as text. To get bash to interpret a value as a number, in order to do arithmetic operations including arithmetic comparisons, you must wrap the expression in double-parentheses. For example, to increment a variable i by one, use the expression((i++)) .

Conditionals and Loops

In addition to basic commands, the Bash shell includes capabilities for conditionals (if) and loops (i.e., fora and while).

If statements

One general syntax for an if-statement follows. As you would probably expect, the elif and else clauses are optional, and the conditions and commands should be replaced with meaningful expressions. Note that the spaces that separate the square brackets from the conditions ARE required.

if [ condition ]; then
  command(s)
elif [ condition ]; then
  command(s)
else
  command(s)
fi

The long history of Bash is particularly evident in the syntax allowed for if expressions. Bash allows both, but interprets each syntax properly — but do not try to mix and match the different versions. The following examples illustrate acceptable Bash syntax, following two different ancestors.

Task Bourne shell style Korn shell style
numeric test
v > 0
if [ $v -gt 0 ]; then
    echo positive
fi
if (( $v > 0 )); then
    echo positive
fi
numeric test
v ≤ 0
if [ $v -le 0 ]; then
    echo non-positive
else
    echo positive
fi
if (( $v <= 0 )); then
    echo non-positive
else
    echo positive
fi
numeric test
v < 0 and v == 0
if [ $v -lt 0 ]; then
    echo negative
elif [ $v -eq 0 ]; then
    echo zero
else
    echo positive
fi
if (( $v <= 0 )); then
    echo non-positive
elif (( $v == 0 )); then
    echo zero
else
    echo positive
fi
numeric test
0 ≤ v ≤ 10
if [ 0 -le $v -a $v -le 10 ]; then
    echo between 0 and 10
else
    echo not between 0 and 10
fi
if (( (0 <= $v) && ($v <= 10) )); then
    echo between 0 and 10
else
    echo not between 0 and 10
fi

For reference, all of these code segments are available in the Bash shell bash-example-1.sh

Comparison Operators

Comparison operators are available in both the Bourne and Korn environments, although the syntax is different:

Korn shell:      ==   !=    <   <=    >   >=   &&   ||
Bourne shell:   -eq  -ne  -lt  -le  -gt  -ge   -a   -o

String comparison is possible only with the symbols

 ==  !=  <  <=  >  >=  &&  ||

Conditionals Regarding Files and Directories

Bash scripts are frequently written for tasks involving the creation and maintenance of files and directories because it can be much faster and easier to write a script for these tasks than to write the corresponding C programs.

There are many operators that can be used for testing conditions that involve files: whether they exist, what kind of file they are, etc. Here are a few. Note that you could also place the NOT operator (!) before any of these tests to test whether the stated condition is false.

condition checks whether...
-f file file exists and is a regular file
-d dir dir exists and is a directory
-x dir file exists and is executable

For example, you might write:

clist="classlist.txt"
if [ -f $clist ]; then
  echo File $clist exists.
else
  echo File $clist does not exist.
fi

for loops

The syntax for a for-loop follows.

for varname in value1 value2 ...
  do
    command(s)
 done

The variable varname will take on each value in the list of values in turn, with one iteration of the loop occuring for each value.

while loops

The syntax for a while loop follows.

while test
do
  command(s)
done

Getting Input

Getting Input From stdin

The command read varname will read a value from stdin (i.e., the terminal) and assign it to a variable called varname. If the variable does not yet exist, it will be created.

Note that the option -n can be used with echo to cause it to not output a newline character. (This is nice when printing user prompts, so the user can enter a reponse on the same line as the prompt).

Getting Input From Commands

Sometimes one wishes to execute a bash shell command or other program and store the result in a variable. This can be done by using the tick quote, which is under the tilde key, ~ and looks like this: `. Note now it is different from an apostrophe (near the enter key), which looks like '.

As an example, one might store the number of words in a file to a variable with the following command

numWords=`wc -w`

Getting Input From Command-line Arguments

As we'll see soon, the mechanism for accessing command-line arguments in a bash script differs substantially from C. In bash, there are several variables that are automatically defined and loaded with information from the command-line as shown below.

variable stores...
$# number of arguments given
$* list of all arguments given
$0 the name of the script
$1 the first argument
$2 the second argument
etc

For example, to verify the number of arguments given you might say:

if [ $# == 3 ]

Bash Quotes

To summarize, Bash scripts allow the use of three types of quotation marks: single quotes ('), back quotes (`), and double quotes ("). The bash shell interprets these variations in different ways.

Quote Type Interpretation
single quotes (') the entire string within single quotes is taken literally, as a single entity (except the explanation point ! and the substring \newline have special meaning)
back quotes (`) the characters within back quotes are considered a Linux command, to be executed before the rest of the line is evaluated.
double quotes (") text within double quotes is scanned for Linux commands and $-sign variables (described above).

These rules may seem complex, so a few examples may help. In what follows, the whoami reports the name of the current user—walker in this example.

echo 'the user is `whoami`'
the user is `whoami`

All text in single quotes is printed without evaluation.

echo "the user is `whoami`"
the user is walker

(Within double quotes, the back-quoted phrase `whoami' is evaluated and inserted into the string before printing.)

echo "the user is whoami"
the user is whoami

(Within double quotes, but without back quotes, the Bash shell does not recognize whoami as a Bash command.)

echo the user is `whoami`
the user is walker

(With no outside quotes, the back-quoted phrase `whoami' is evaluated and inserted during printing.)

You can verify this yourself with bash-script-2.

Some Additional Commands

Bash includes a huge range of commands. Here are a few more useful capabilities:

Command Description Example
cal display a calendar for a given year cal 2010
date print the current time and date in various formats
many options documented with man date
date +'%I:%M %p on %A, %B %d, %Y'
diff display differences in lines between two files diff file1 file2
echo print specified text echo hello world!
env print the environmental variables currently set env
grep scan a file or other input for a specified text env | grep home
hostname return name of current workstation hostname
sort sort lines of a file sort -n -k 12 (sort by numbers in column 12)
users simple list of users users
who print list of users currently logged in who and who -a
whoami print the username of the person logged in whoami
ypcat password print user information from the password file ypcat password | grep $USERNAME

More information on each of these commands may be found with the man facility (e.g., man date). Beyond this basic documentation included here an in previous labs, many resources are available on line. Here are a few places to begin: