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.
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:
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.#!/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"
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"
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
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
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
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 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
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.
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.
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.