Intro to Shell Scripts

Why write shell scripts?

To really master Linux it is important to be able to understand shell scripts. A lot of items you will come into contact with could be a script. Also, at times you might have to do a task that is repetitive. Instead o f having to redo the task manually each time you could write a script that'll handle it for you. This makes your job a bit easier. Another great reason to learn shell scripting is that IT will help you learn the CLI better. Also, it will help you with career progression if you want to become a sysadmin or even get into cybersecurity.

When I am working on writing a shell script I break the task down in to smaller tasks. I then focus on one task at a time. I also really like to run the commands on the command line first and debug them that way. This makes life a bit easier as I can keep retrying until I have it working correctly. Once I have the command exactly as I want I will then copy and paste it into the script file. I also like to build the commands slowly, what I mean by this is that if I know I'll need to pipe (|) commands together I do the first part and make sure it is correct, I then will pipe the output to another command and make sure I am getting the output I except. Once I have that correct I will then pipe the data to another command and continue this process until I have exactly what I need/want. This process does take time, but in the long run it is easier as you don't have to debug a long complex command as you are doing the debugging process as you build the command.

Shell Script Examples

As you have taken ICS 111 or are almost completed with taking ICS 111 you should have a basic understanding 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 short story about how I used shell scripting 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.

Script: hello1.sh

#!/bin/bash

echo Hello World

Explanation

As this is our first script I'll explain the components of it. To start with the name of the shell script is not important, but you should name it something meaningful. 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 the path to the 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.

chmod +x hello1.sh

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

Output:

Hello World

hello2.sh

#!/bin/bash

STR="Hello World!"

echo $STR

Explanation

The the new elements in the shell script will be explained going forward. Please reference previous scripts if you have questions about an element.


A variable called STR (just like environmental variables, you should create your script variables in capital letters) was created and I've assigned it the value "Hello World!".

Scripting is very strict, if you have spaces between the equals sign and the value (i.e. STR = " Hello World! ") it will not work and provide you with an error. To print out the contents of a variable use echo and the variable name, preceding it with a $: echo $STR

Output:

Hello World!

conditional.sh

#!/bin/bash


VAR1="Foo"

VAR2="Bar"


if [ $VAR1 = $VAR2 ]; then

echo true

else

echo false

fi

Explanation

The new element being introduced in this shell script is a conditional statement (if statement). Notice the format of the if statement, again spacing matters and it differs from Java as we use [ ] (square braces) and not ( ) (parentheses). Also, notice that after the square brace ends we have a ; and the word 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. To learn about the operators that can be used please read Other Comparison Operators as this provides an explanation and examples of usage.

Output:

false

for.sh

#!/bin/bash


for i in $( ls ); do

/usr/bin/file $i

done

Explanation

The for loop in shell scripting is a bit different then what you are use to in Java. It still has the same idea of only executing for a specific number of iterations. What this code is going to do is run the ls command in the same directory as the shell script, 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 will provide the current element in the list from the ls command.

Another new element being introduced is 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 absolute path to the binary, this provides better scripting practices and makes sure you are using the proper binary file.

Output:

$> ./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

while.sh

#!/bin/bash


COUNTER=0


while [ $COUNTER -le 10 ]; do

echo $COUNTER

let COUNTER=COUNTER+1

done

Explanation

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 performing and this is how we can manipulate values of 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 does.

Output:

$> ./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

Explanation

The until is a new type of loop. It has the same idea of a while loop, but this loop executes until the conditional check evaluates to true. This loop will count down from 20 until it reaches 10.

Also, notice the short hand method for variable assignment in the let statement. You should be familiar with this from taking ICS 111.

Output:

$> ./until.sh


20

19

18

17

16

15

14

13

12

11

10

example1.sh

#!/bin/bash


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

echo $FILE

Explanation

This is a very short shell script, but it is introduces a new and powerful concept. The script shows how you can use other commands when creating a variable.

I created a variable called FILE and in here I want to create a filename 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 we have a $ and () with the command we wish to use inside the (). This command will be executed and the value will then be replaced when assigning a value to the variable.

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.

This script does not actually create the file.

Output:

$> ./example1.sh

/Users/grossp/backup-20220711.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

Explanation

Recall, I mentioned at 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 use the grep command to obtain the single line I need. Once I have a single line of data 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. As we used the tr command 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 the 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 the method we will perform. 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):

$> ./third.sh 5

192.168.1.1

Checking for connectivity.

PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.

64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=1.02 ms

64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=9.09 ms

64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=1.12 ms

64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=1.05 ms

64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=7.88 ms

--- 192.168.1.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


$> ./third.sh 2

192.168.1.1

Checking for connectivity..

PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.

64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.980 ms

64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=1.03 ms

--- 192.168.1.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

Reading and executing shell scripts to see the output is one of the best ways to learn how to create shell scripts. Shell scripts are often 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 the basics of 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.

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. In other words, I slowly build the needed command on the command line before I place it in my shell script. This helps in debugging or finding issues that need to be resolved before moving forward to the next task.

Shell Script Basics

Please view each of the following URLs as they will help to provide additional examples and expand your knowledge of shell scripts.

https://www.shellscript.sh/

https://linuxhint.com/30_bash_script_examples/

https://www.xmodulo.com/arithmetic-operations-bash.html

https://www.redhat.com/sysadmin/arguments-options-bash-scripts

https://dev.to/bobbyiliev/the-only-bash-scripting-cheat-sheet-that-you-will-ever-need-55c7

As shell scripts are plain text files you might want to encrypt/hide the contents of the script. This is possible to do by encrypting the shell script. Please read the tutorial below to learn how to encrypt a script.

https://www.xmodulo.com/encrypt-shell-script.html