Module Eight - Session 26: Shell Scripts

What will I be learning?

We will learn about shell scripting. This will be the major focus of this module. One of the reasons we will focus on shell scripts in detail is the Linux startup scripts are actually shell scripts. Being able to read/understand these scripts is important. Also, being able to create your own will allow you to create or modify scripts of your own as needed.

After this chapter I should be able to...

You should be able to write a basic shell script to perform a simple task. Your basic shell script should contain variables, conditionals and/or looping.

What should I practice?

As you have taken ICS 111 you should understand the basics of programming. You should understand what a for loop does, what a while loop does, and conditional statements. A variable shouldn't be an unknown entity for you. Shell scripting is a way of programming and automating things on your Linux system to make your life easier. Here is a quick little story about how I used shell scripting recently to save time.

I was asked to help rename years worth of files into a standard naming convention. I was asked to rename about 1/3 of all the files and it had to be close to 75-100 files (it was years worth). If I didn't know how to write a shell script I would have had to manually rename all the files one by one. Instead, I wrote a shell script, extracted the data I needed from the file name and I then renamed the file as I wanted it to be named. I ran the file in a directory that contained all the files that needed to be renamed. It took me maybe 15-20 minutes to create the script, and took about 2 minutes for the script to run. Then using the script I completed the other 2/3 of the files for my peers to make their life easier. Something that people thought would take days to complete I did in under an hour of time. Enough story time, let us jump into some basics of shell scripting.


hello1.sh

#!/bin/bash

echo Hello World

I'll explain the components of the shell script. To start with, the name of the shell script is not important. I've placed the name of the shell script, which is just a plain text file as the heading for the section. The first line of the file is the shebang (#!) and then shell we wish to use. You can use any type of shell you wish, you just need to make sure to provide the path to it.

The next line we have is an echo statement. This will display the text after it, notice we don't have any quotes or a semicolon to end the line.

Once you've created your shell script and saved it, you have to make sure it is executable, so give it the proper permissions.

You can then run the shell script be doing: ./hello1.sh.

Output: Hello World


hello2.sh

#!/bin/bash
STR="Hello World!"

echo $STR

From now on, I'll just explain the new elements in the shell script. Notice, I've created a variable (STR) and I've assigned it the value "Hello World!". Scripting is very strict, if you have spaces between (i.e. STR = " Hello World! ") it will not work and provide you with an error. To print out the contents of a variable just use echo and the variable name, preceding it with a $. Variables do not need to be upper case, I just find it easier to read and recognize variables this way in shell scripting.

Output: Hello World!


con3.sh

#!/bin/bash

VAR1="Foo"
VAR2="Bar"

if [ $VAR1 = $VAR2 ]; then
    echo true
else
    echo false
fi

The new element being introduced in this shell script is a conditional statement. Notice the format of the if statement, again spacing matters. This differs from Java as we use [ ] (square braces) and not ( ) (parentheses). Also, notice that after the square brace ends we have a ; and the work then. We end a if statement with the reverse, fi. It is important to understand these differences, and get familiar with them.

Notice the conditional check is using two variables and the single equal sign, scripting does not use a double equals (==) to do comparison, another major difference. Here is a nice listing of the comparison operators that can be used in scripting.


for.sh

#!/bin/bash

for i in $( ls ); do
    /usr/bin/file $i
done

The for loop in shell scripting is a bit different. But, still has the same idea of only executing for a specific number of iterations. What this code is going to do is run ls in a directory, and loop through each element in the directory and perform the file command on it. A for loop in scripting is not a counter as we've done often in Java, but it determines a number of elements and loops that many times until each one has been visited. The i is a variable, and this will hope the current element in the list from ls.

Another thing you notice is that within shell scripts you can actually execute other shell commands, such as here I'm using /usr/bin/file. When using other commands make sure to use the path to the binary, this provides better scripting practices and makes sure you are using the proper binary file.

Output:
[grossp@~/Dropbox/Leeward/ICS/ICS 240/shell scripts ]> ./for.sh
catherine.sh: Bourne-Again shell script text executable
con1.sh: Bourne-Again shell script text executable
con2.sh: Bourne-Again shell script text executable
con3.sh: Bourne-Again shell script text executable
coolif.sh: Bourne-Again shell script text executable
example3.sh: Bourne-Again shell script text executable
first.sh: Bourne-Again shell script text executable
for.sh: Bourne-Again shell script text executable
hello1.sh: Bourne-Again shell script text executable
hello2.sh: Bourne-Again shell script text executable
rename.sh: Bourne-Again shell script text executable
second.sh: Bourne-Again shell script text executable
third.sh: Bourne-Again shell script text executable
until.sh: Bourne-Again shell script text executable
while.sh: Bourne-Again shell script text executable

while.sh

#!/bin/bash

COUNTER=0

while [ $COUNTER -le  10 ]; do
    echo $COUNTER
    let COUNTER=COUNTER+1
done

A while loop works just the same, we test our conditional and make sure it holds true, if it holds true we execute the code within the while loop. If it doesn't hold true, we exit the loop and continue in the script. If you recall, the -le is saying less than or equal too. We've created a COUNTER variable equal to 0, and our conditional statement is asking if these value is less than or equal to 10. While it is we print out the value, and increase it by 1. That is what the let COUNTER=COUNTER+1 statement is saying, this is how we can manipulate variables within a script. If we did not have the let keyword it would not work. The let keyword does left to right assignment instead of right to left like Java would.

Output:
[grossp@~/Dropbox/Leeward/ICS/ICS 240/shell scripts ]> ./while.sh
0
1
2
3
4
5
6
7
8
9
10

until.sh

#!/bin/bash

COUNTER=20

until [ $COUNTER -lt 10 ]; do
    echo $COUNTER
    let COUNTER-=1
done

This is a new type of loop. It has the same idea of a while loop, but this loop will execute until the conditional check evaluates to true.

Also, notice the short hand method for variable assignment in the let statement.

Output:
[grossp@~/Dropbox/Leeward/ICS/ICS 240/shell scripts ]> ./until.sh
20
19
18
17
16
15
14
13
12
11
10

example3.sh

#!/bin/bash

FILE=~/backup-$(/bin/date +%Y%m%d).tar

echo $FILE

This is a very short shell script, but it is bringing a new concept to you. You can actually use other commands when creating a variable name. I've created a variable called FILE and in here I want to create a file that has the following format: backup-YYYYMMDD.tar. I can obtain this by using the date command and providing the proper options (as you see in the code). Notice the name of the file that is printed, it has the proper format that I wished. I could then use this with tar to create a back up file of a specific directory.

Output:
[grossp@~/Dropbox/Leeward/ICS/ICS 240/shell scripts ]> ./example3.sh
/Users/grossp/backup-20150726.tar

third.sh

#!/bin/bash

ip=`route -n | grep UG | tr -s " " | cut -f 2 -d " "`
echo $ip

echo "Checking for connectivity.."

ping -c $1 $ip

Recall, I mentioned way back in the start of the semester that some commands will be more helpful and useful with shell scripts. Well, here is one of those times. The idea behind this script is to determine if I can ping my default gateway. I can get my default gateway by using the route -n command. Though, this also provides me with extra information. I know that from this command that the flags for my default gateway will be UG. So, I can grep for this and single out that one line that I want. Now, I just need to get that IP address. Recall, tr will delete characters, so I say squeeze each of the repeated " " (space) character to a single one. This makes it a lot easier to use the cut command. I just next need to determine what field I need to specify for the cut command. Well, because we used the tr and deleted all the extra spaces it will be 2 when we use a delimiter of " " (space). Doing this we know have our default gateway stored in the ip variable.

Next, we will want to ping our default gateway to see if it is reachable. We could just ping it until we manually stopped it or we can provide a command line argument to our shell script for the number of times we wish to ping. This is what we will do. Command line arguments to shell scripts are stored in the following way: $0, $1, $2, .... $n.

$0 will be the name of your shell script

$1 will be the next item on the command line being passed to your script

$2 will be the item after $1 and this continues for how ever many values you pass.

This is why I am using $1 in the ping command, as this will be the number of times to send a ping packet.

Output (code was run two times to show different values being passed):

[grossp@~/Dropbox/Leeward/ICS/ICS 240/shell scripts ]> ./third.sh 5
192.168.144.1
Checking for connectivity..
PING 192.168.144.1 (192.168.144.1) 56(84) bytes of data.
64 bytes from 192.168.144.1: icmp_seq=1 ttl=64 time=1.02 ms
64 bytes from 192.168.144.1: icmp_seq=2 ttl=64 time=9.09 ms
64 bytes from 192.168.144.1: icmp_seq=3 ttl=64 time=1.12 ms
64 bytes from 192.168.144.1: icmp_seq=4 ttl=64 time=1.05 ms
64 bytes from 192.168.144.1: icmp_seq=5 ttl=64 time=7.88 ms

--- 192.168.144.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4004ms
rtt min/avg/max/mdev = 1.024/4.036/9.092/3.657 ms

[grossp@~/Dropbox/Leeward/ICS/ICS 240/shell scripts ]> ./third.sh 2
192.168.144.1
Checking for connectivity..
PING 192.168.144.1 (192.168.144.1) 56(84) bytes of data.
64 bytes from 192.168.144.1: icmp_seq=1 ttl=64 time=0.980 ms
64 bytes from 192.168.144.1: icmp_seq=2 ttl=64 time=1.03 ms

--- 192.168.144.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.980/1.006/1.032/0.026 ms

I feel that looking at shell scripts and seeing the output is one of the best ways to learn how to create shell scripts. Shell scripts a lot of the times are something we create as quick and dirty code to complete a specific task once (such as my story above, I'll probably never use that code again). With your understanding of programming, I hope that these examples and explanations provide you with the knowledge you need for shell scripting.

While you are looking through these examples, make sure you pay attention to how the formatting differs from Java. This is important, as scripting is strict and won't work if you don't follow the requirements.

Also, one last tip... when I am writing shell scripts I test the command out before I included it in my script. So, when I was creating the third.sh script I figured out what I had to do using route, grep, tr and cut before I even placed it into the script file.


Original webpage by Petersen Gross, modified by William Albritton.