Brought to you by molecularsciences.org.
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 License.
This publication may not be redistributed without this notice.

Bash Shell Scripting

This ebook is contributed by Mehrosh Pervez

A shell script is a code (script) written for the shell or command line interpreter of an operating system such as Unix. It allows users to automate tasks which would be cumbersome to execute manually. For example renaming 2000 files. You could do it by 2000 mv commands or write up a 3 line shell script to do it for you.

A shell script can be written in any shell scripting language installed on the system. The most widely used shell is the Bash Shell. Others include csh, ksh, tcsh, etc. Here we would only be discussing bash shell.

Getting Started

Which shell I am running?

$ echo $SHELL
/bin/bash

This command prints the address of the shell you are running.

How do I switch to Bash Shell?

bash

Which shells are installed on my system?

$ cat /etc/shells
# /etc/shells: valid login shells
/bin/csh
/bin/sh
/usr/bin/es
/usr/bin/ksh
/bin/ksh
/usr/bin/rc
/usr/bin/tcsh
/bin/tcsh
/usr/bin/esh
/bin/dash
/bin/bash
/bin/rbash

First Example

clear; echo "This months calendar"; cal;

A shell can script can be run on commandline from a file. You can use any text editor which doesn't insert formatting into text to write a shell script. vi and emacs are good editors. Save the following code in file called test.sh:

clear
echo "This year's calendar"
cal -y

By convention, shell scripts are saved with .sh extension. Change permissions of the file:

chmod 755 test.sh

You can use any one of the following commands to execute your bash script.

bash test.sh
sh test.sh
./test.sh

Documenting in code
It is important to get into the habit of documenting the code. Following is a 'hello world' example:

#!/bin/bash
#: Title         : hello world
#: Date         : 2010-09-22
#: Author      : "Nazim Rahman"
#: Version     : 1.0
#: Description : print hello world
#: Options     : None

printf "%s\n" "hello world"
The first line of code beginning with a shebang (#!) is a call to the bash interpreter. It tells the system which interpreter it should used to execute the code. All lines starting with a hash (#) symbol are comments. The last line of code prints the string hello world. %s means string and it is referring to the string "hello world". The newline character (\n) specifies end of line. Anything typed after it would be printed on the next line. printf in BASH is like printf in C.

Bash Variables

Bash uses two kinds of variables.

To see a list of system variables type:

set

This would give you a list of system variables. DO NOT change values of any of these variable unless you know what you are doing. Unlike Microsoft windows, Linux allows you more control over you system assuming that you know what you are doing. So, if the root granted you a rather unrestrictive access, you can create many problems for yourself.

To create a user defined variable 'number', for example:

number=10

You can use alphanumeric characters and underscore to for variable names. Variable names are case-sensitive. You many not use spaces in assignment. Following are not allowed.

number =10

number= 10

number = 10

To assign NULL values:

budget=""

budget=

To print variables

echo $number

Example

year="427 BC"
name=Plato
city=Athens
echo "$name was born in $city around $year"

Printing Parameters

A parameter is an entity that stores value and is available to a program. There are many system variables available to BASH. They could be retrieved easily by prefixing a $ sign to the variable. Alternately, you can also surround them with curly braces and prefix a $ sign

#!/bin/bash
#: Title       : print parameters
#: Date        : 2010-09-22
#: Author      : "Nazim Rahman"
#: Version     : 1.0
#: Description : print parameters
#: Options     : None

printf "%s " "Current Working Directory"
echo $PWD
printf "%s " "Home Directory"
echo ${HOME}
printf "%s" "Command Directories $PATH"

Output of the script

Current Working Directory /home/me/sandbox
Home Directory /home/me
Command Directories  /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Using Positional Parameters

On your terminal, type the following command

cal -y 2012

This command would print the calendar for 2012. -y and 2012 are positional parameters. Positional parameters are used to pass arguments to the program. The arguments become available to program as numbered parameters. In the above command, the arguments are passed as follows:

$0 = cal
$1 = -y
$2 = 2012

Following is an example:

#!/bin/bash
#: Title       : positional parameters
#: Date        : 2010-09-22
#: Author      : "Nazim Rahman"
#: Version     : 1.0
#: Description : show how to print and use positional parameters
#: Options     : war and years
#: Example     : $ ./upp.sh WWII 1945
 
printf "Filename: %s\n" "$0"
printf "First Argument: %s\n" "$1"
printf "Second Argument: %s\n" "$2"
printf "%s ended in %s\n" "$1" "$2"

Type the following command to

$ ./upp.sh WWII 1945

Output

Filename: upp.sh
First Argument: WWII
Second Argument: 1945
WWII ended in 1945

Reading input using the read command

read is a built-in shell command that reads from stdin. See the example below:

$ read war year
WWI 1918
$ ./upp.sh $war $year
Filename: upp.sh
First Argument: WWI
Second Argument: 1918
WWI ended in 1918

read created two variables $war and $year. User assigned the values WWI and 1918. These variables were passed as parameter to the program we created in the previous section.

read command is very handy with files. Create a file called data.txt and add the following line to it.

cold-war 1991

Then run the commands as below:

$ read var < data.txt
$ ./upp.sh $var

Output

Filename: upp.sh
First Argument: cold-war
Second Argument: 1991
WWI ended in 1991

Escaping single quotes inside single quotes

In bash, you can use the backslash character ("\") to escape any character. However, if a single quote is inside single quotes, it cannot be escaped with a backslash character. For example:

alias ycd='some_command'

works fine but

alias ycd='command -option 'something''
alias ycd='command -option \'something\''

both of the above return errors.

A trick around this problem is to use both quotes i.e. use double quotes to surround the single quote and single quotes again to surround the single quote.

using ' is that same as '"'"'

To rewrite the example above"

alias ycd='command -option '"'"'something'"'"''

Note that the first and last single quotes are not converted.

for loop in bash

For loops in bash a similar to commonly used languages such as Perl and C. bash loops do not use curly brackets for body demarcation.
Example 1

#!/bin/bash
#: Title       : for
#: Date        : 2011-07-11
#: Author      : "Nazim Rahman"
#: Version     : 1.0
#: Description : for loop example
#: Options     : None

clear
for i in 1 2 3 4 5 6 7 8 9 
do 
    echo "printing number $i"
done

Loop start from the do statement and ends with the done statement. This code prints numbers as follows:

printing number 1
printing number 2
printing number 3
printing number 4
printing number 5
printing number 6
printing number 7
printing number 8
printing number 9

Example 2

j=1;
for i in 1 2 3 4 5 6 7 8 9 
do
    j=$j*$i 
    echo $j
done

You would expect this code to multiply numbers and print their results but this code concatenated the values instead. bash treats all variables as strings and there is no casting. The output of the program is as follows:

1*1
1*1*2
1*1*2*3
1*1*2*3*4
1*1*2*3*4*5
1*1*2*3*4*5*6
1*1*2*3*4*5*6*7
1*1*2*3*4*5*6*7*8
1*1*2*3*4*5*6*7*8*9

Example 3

j=1;
for i in 1 2 3 4 5 6 7 8 9 
do
    j=`echo "$j * $i" | bc` 
    echo $j
done

This code actually does what we expected the previous one to do. It multiplies variables and prints the results. We used echo to write the mathematical equation e.g. 2 * 3, and then pipe the string to bc. bc is a commandline calculator and it returned the value 6 which was stored in the variable j as a string and then printed to the screen with the following line of code.

1
2
6
24
120
720
5040
40320
362880

Changing file extensions

Suppose I need to change the file extensions of certain files in a directory.

#!/bin/bash
echo "Please specify a directory: "
read folder
cd $folder
for i in *.txt
do
mv "$i" `basename $i txt`fasta
done

The directory address specified by the user is read into the script in line 2. This address is used to cd to the directory. This script changes the extensions of .txt files to .fasta.

Renaming Files in Directory

Suppose you have 2000 photos in a directory and you need to rename them. The files are named as:

IMG_1232.JPG
SP234324.JPG
webdown.JPG
...

We need to rename them as follows:

salzburg_1.jpg
salzburg_2.jpg
...

Here is how:

j=1
s=salzburg_
for f in *.JPG; do
  mv $f $s$j.jpg
  ((j=$j+1))
  echo renaming $f to $s$j.jpg
done

j in is the counter. Line 5 increments j. s is a string. Line 4 uses the mv command renaming each file to the desired combination of text and number. Line 6 prints a message on the command line. Note that in line 3, we only loop through files ending in .JPG.

To specify name from commandline:

j=1
s=$1
for f in *.JPG; do
  mv $f $s$j.jpg
  ((j=$j+1))
  echo renaming $f to $s$j.jpg
done

To run

$ ./rename salzburg_

The commandline is passed into $@ array where $0 is the command followed by first argument $1, then second argument $2, and so on.

Sorting and Merging files

The following script merges several files into one file.

#!/bin/bash
echo "Please specify a directory"
read folder
cd $folder
ls | sort -n
for i in *.fasta
do
cat $i >> virus
done

Line 5 creates a sorted list of directory contents. Line six loops through all files with .fasta extension. Line 8 appends contents of every file to the file virus.