Technology Inside Out!

Index ¦ Archives ¦ Atom ¦ RSS

Introduction to Shell Script

Ever wanted to automate a task on your *nix machine? Or are you fed up executing some set of commands again and again? If yes, then shell scripting can help you out there. In this tutorial, introduce yourself to shell scripting, along with some hands-on examples to practice.

What is a shell?

The shell is a user program or it is an environment provided for user interaction. It is a command language interpreter, that executes commands read from the standard input device such as a keyboard or from a file.

Several shells are available for Linux including:

  • BASH ( Bourne-Again SHell ) - Most common shell in Linux. It's Open Source.
  • CSH (C SHell) - The C shell's syntax and usage are very similar to the C programming language.
  • KSH (Korn SHell) - Created by David Korn at AT & T Bell Labs. The Korn Shell also was the base for the POSIX Shell standard specifications.
  • TCSH - It is an enhanced but completely compatible version of the Berkeley UNIX C shell (CSH).

A shell is simply accessible using a terminal. If you are familiar with *nix, then you might have an experience of using it. Now, if you want to know what all shells are available in your system, then use the following command.

$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash
/bin/zsh
/usr/bin/zsh

What is a shell script?

It is a programming language of shells, where you may write a set of commands to execute for a particular shell. And similar to programming, shell scripts support that sequential, conditional and iteration flow along with functions that help to make your programs more modular.

Shell Program: It is simply the file containing shell commands. You may directly run this file instead of individually executing each command.

Please note that each shell does the same job, but each understands different command syntax and provides different built-in functions. In this tutorial, we'll discuss shell scripting w.r.t bash. Because "bash" is used as a default shell in most *nix systems.

Use cases of shell scripting

The following tasks could be done easily with shell scripting.

  • Automating a series of tasks:
    Like loading script files according to the availability of shells found in your system. Or loading OS-specific settings. (here loading means to source/run a file)
  • Setting cron/anacron jobs:
    Like every week, auto removing packages that are no longer usable.
  • Executing a set of commands.
    Like installing no. of packages, without repeatedly giving your confirmations (y).

Commands you write in a script can also be executed directly on a shell, and commands you write in the shell can also be written in the script. Therefore, you may do everything with scripting, that you may think of doing with a shell.

Writing your first script

To create a shell program you need to go through the following steps:

1. Create a file for the shell program.

$ touch newfile.sh

Here, we have given the extension .sh  or .bash (conventional for bash script), though it is not necessary, but quite useful to distinguish between scripts of different shells.

2. Put a shebang (#!) on the first line of the file

1
#!/bin/bash

Shebang(#!) is a special comment, that informs, which shell to be selected while executing this file. It is actually the path to the shell required to run this script.

3. Add some commands to the file

echo "Script is working fine!"

4. Save the file.

5. Make the file executable and run the program.

To run a script file, at least it must have the permission of "readability" so that we can explicitly execute it using a shell.

$ bash newfile.sh
Script is working fine!

And if the script file is "executable", then we may directly run the file as shown below.

$ chmod +x newfile.sh
$ ./newfile.sh 
Script is working fine!

Hurray! You got your first shell script running. Let's go ahead and explore more programming elements of scripting.

Comments

Writing comments is really useful in any programming language. Because the code is read much more times than it is written. Also, comments make the code easy to comprehend, thus, it is advisable to use comments to explain complex parts of the code.

In shell scripting, you may give comments with hash (#) symbol.

# File: newfile.sh
# Purpose: To learn and practice shell script
# Author: Your name
# These are comments in a shell script.

Shell Parameters

Values you pass to a shell program from a terminal while executing the script, are called program parameters. For example.

$ ./newfile.sh Hello World

Here, "Hello" and "World" are 2 parameters that are passed to newfile.sh. You may pass any number of parameters to a shell script. But, the question arises, how are we gonna use them in a program?

\$0 to \$9

  • \$0: It holds the complete path to the script file from the current working directory.
  • \$1-\$9: These hold shell parameters. For example: \$2 is second parameter.
  • \$#: It holds the total number of shell parameters (i.e exclude \$0).

To access these parameters we may use dollar (\$) symbol followed by the number of parameter. For example, if your file is.

1
2
3
4
#!/bin/bash
echo "Running script $0"
echo "First parameter is $1"
echo "Second parameter is $2"

Output:

$ ./newfile.sh Hello World
Running script: ./newfile.sh
First parameter is: Hello
Second parameter is: World

Along with these you also have other predefined variables.

\$* and \$@

  • \$*: It has all the parameters passed as a single word, where each element is space separated.
  • \$@: It has all the parameters passed but it holds each parameter as a separate word.

\$* and \$@ seems confusing from the definition, therefore, let's take a look at the example below.

1
2
3
4
5
6
7
#!/bin/bash
echo "With \$*"
echo "Total parameters: $#"
for i in "$*"
do
   echo "$i"
done

The output is.

$ ./newfile.sh 1 "2 3" "4 5"
Total parameters: 3
With $*
1 2 3 4 5

See, here total parameters are 3, but you are actually getting 1 string that includes all parameters.

Note: While using \$*, if the parameter is "2 3", then it is treated as "2" and "3". This means the shell interprets the space symbol. But, this is not the case with \$@.

If you use "\$@" instead of "\$*" you'll get the following.

$ ./newfile.sh 1 "2 3" "4 5"
Total parameters: 3
With $@
1
2 3
4 5

Here, 3 separate parameters can be easily seen.

\$\$ and \$?

  • \$\$: It shows the process ID. Following command shows the process ID of the current terminal.

    $ echo $$
    2123
    
  • \$?: It holds exit status of the last command. For example.

    $ clear
    $ echo $?
    0
    

    Here, 0 indicates that last command (clear) was successful. In case of failure, \$? returns any non-zero value.
    To return exit status 0 or 1 from your shell script, simply use the exit command.

    1
    2
    3
    #!/bin/bash
    echo "Successful"
    exit 0
    

Shift Command to access more than 9 parameters

You may easily go from \$1 to \$9. But how would you use the 10th parameter?

Accessing 10th parameter is not possible with "\$10" because the shell will interpret only the first digit, that is "1", and output of this statement would be the first parameter followed by a "0".

To solve this issue we have shift command. This command shifts all the parameter to left by one. As a result, it also decreases the number of total parameters (\$#). For example.

1
2
3
4
5
6
7
#!/bin/bash
for i in 1 2 3 4
do
   echo "Total parameters: $#"
   shift
   echo "Parameters: $*"
done

The output of this command.

$ ./newfile.sh a b c d e f
Total parameters: 6
Parameters: b c d e f
Total parameters: 5
Parameters: c d e f
Total parameters: 4
Parameters: d e f
Total parameters: 3
Parameters: e f

We can see, that parameters are shifting to left and getting reduced by one. So, if you want to access 10th parameter then you need to left shift once and access \$9. However, this command is not much recommended due to the side effect of losing earlier parameters.

Variables and Expressions

Expressions are valid combination of variables and operators.

Variables: These are shell parameters or any other initialized variable in a shell program like.

1
2
3
#!/bin/bash
var=10  # An integer variable
str='Hello'  # A string variable

Note: No space around "=" (assignment) operator.

Operators: These are special symbols to operate on variables. For example.

String operations


-z string True: if the length of a string is 0. -n string True: if the length of a string is not 0. string1 = string2 True: if the two strings are identical. string != string2 True: if the two strings are NOT identical. string True: if the string is not NULL.


Integer operations


int1 -eq int2 True: if the first int is equal to the second. int1 -ne int2 True: if the first int is not equal to the second. int1 -gt int2 True: if the first int is greater than the second. int1 -ge int2 True: if the first int is greater than or equal to the second. int1 -lt int2 True: if the first int is less than the second. int1 -le int2 True: if the first int is less than or equal to the second.


File operations


-r file True: if the file exists and is readable. -w file True: if the file exists and is writable. -x file True: if the file exists and is executable. -f file True: if the file exists and is a regular file. -d file True: if the file exists and is the directory.


Logical operations


! reverse the result of expression -a AND operator -o OR operator


Conditional Flow

When program control flows sequentially from start to end of a program, it is known as the sequential flow of programming. But, in case of conditional flow, the program is executed on the basis of conditional statements. If the result of such statements is true, one part of the code is executed, and if the result is false, another part of the code is executed, not both. This is called "Mutual Exclusion".

Shell script provides 2 conditional statements 1) if-else and 2) case. Let's have a look at their syntax and usage.

If-else statement

Usage: To execute a set of commands, on the basis of evaluation of an expression.

Syntax:

if command1    # if block started
then
     command-list1
elif command2  # optional
then
     command-list2
else           # optional: to accept rest of the conditions.
     command-list3
fi             # end of if-else block

Here, elif and else parts are optional. This implies if command1 returns success (0), then execute command-list1. Otherwise, if command2 returns success (0), then execute command-list2, and if no condition turns out to be successful, then execute command-list3.

Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/bin/bash
if ls | grep ^z > /dev/null
then
   echo Firt case.
elif ls | grep ^a > /dev/null
then 
   echo Second case.
else
   echo Third case.
fi

Here, "ls | grep \^z"  searches files/folders in the current directory that starts with "z". And "> /dev/null" sends the output of the file in a null device (typically used for disposing of unwanted output streams of a process).

The output of the above program is shown below. (As I don't have any file that starts with "z")

Second case.

Test Condition

Usage: It is mostly used in association with if-else to evaluate expressions instead of commands.

Syntax: test expression or [ expression ]

Example:

if [ expression ]
then
     command-list1
fi

Let's put all this in a short example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/bin/bash
if [ "$#" -gt 1 ] # if total number of parameters exceed 1
then
   echo "Sorry, you entered too many parameters."
elif [ "$#" -eq 1 -a "$1" = "1" ] # if only one parameter is 1
then 
   echo "Hi! Nice to meet you!"
elif [ "$#" -eq 1 -a "$1" = "2" ]  # if only one parameter is 2
then 
    pwd
elif [ "$#" -eq 1 -a "$1" != "1" -a "$1" != "2" ] # if only one is parameter is neither 1 nor 2
then
   echo "You entered the wrong parameter."
else            # if the total number of parameters is below 1
   echo "Sorry, you entered fewer parameters."
fi

Output:

$ ./newfile.sh
Sorry, you entered fewer parameters.
$ ./newfile.sh 1
Hi! Nice to meet you!
$ ./newfile.sh 2
/home/thegeekyway
$ ./newfile.sh 3
Sorry, you entered the wrong parameter.

Case statement

Usage: Shell script also supports case conditional statement. It is used to evaluate a particular variable with some set of values.

Syntax:

case value in
     pattern1)     command-list1;;
     pattern2)     command-list2;;
     *) command-list2;;   # this is optional: to accept rest of the conditions
esac

Example: Let's make the above command using case statements.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/bash
case $# in
     "0") echo "You entered fewer parameters."
          exit;;
     "2") echo "You entered too many parameters."
          exit;;
esac

case $1 in
     "1") echo "Hi, nice to meet you!";;
     "2") pwd;;
     *) echo "You entered the wrong parameters.";;
esac

The output is same as above.

Repeated action commands (Loops)

To write a set of commands, which repeat themselves until a condition is satisfied, we have loops in programming languages. Bash supports 3 types of loops:

  • For loop
  • While loop
  • Until loop

For Loop

Usage: When iterating any variable over an iterator like "\$@", we use "for-loop".

Iterators are objects, having multiple values within them like lists in python. Therefore, using a for-loop we may access their individual values one by one.

Syntax:

for var in iterator
do
    command-list
done

Example:

1
2
3
4
5
#!/bin/bash
for var in "$@"
do
    echo "$var"
done

Output:

$ ./newfile.sh The Geeky Way
The
Geeky
Way

While Loop

Usage: To repeat a set of commands, till an expression remains true.

Syntax:

while [ expression ]
do
     command-list
done

Example:

1
2
3
4
5
6
7
#!/bin/bash
var=0
while [ $var -le 3 ]
do
      echo "$var"
     ((var++))
done

Output:

$ ./newfile.sh
0
1
2
3

Until Loop

Usage: It is reverse of while loop, it repeats a set of commands, untill an expression becomes true.

Syntax:

until [ expression ]
do
     command-list
done

Example:

1
2
3
4
5
6
7
#!/bin/bash
var=0
until [ $var -eq 3 ]
do
   echo "$var"
   ((var++))
done

Output:

$ ./newfile.sh
0
1
2

Read input at runtime

Read Command: To read input from the user at runtime of shell script.

Syntax: read var1 var2

This command will hold (pause) the script to take input from the terminal. Once you give appropriate values, the shell script would run ahead.

Example:

1
2
3
4
5
6
#!/bin/bash
read var
for i in $var
do
   echo "$i"
done

Output:

$ ./newfile.sh
The Geeky Way
The
Geeky
Way

Functions

Usage: These are set of commands, which can be called any number of times in a script. Thus, it gives the feature of re-usability of code and makes the code modular.

Syntax:

function_name()
{
command-list
return value    # optional
}

Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
confirm()
{
echo -e "Are you sure, that you want to continue (y)?: \c"
read var
if [ $var = 'y' ]
then
   return 0
else
   return 1 
fi 
}

if confirm -eq 0
then
    echo "Continue process!"
else 
    echo "Discontinue process!"
fi

Note that, no test expression in the last block of "if-else", because calling function is a command in itself. And we put expressions in the test, not commands.

Output:

$ ./newfile.sh
Are you sure, that you want to continue (y)?: y
Continue process!!

Conclusion

Knowledge of shell scripting is a required norm, not only for Linux administrators but for a general Linux user as well.

Being a Linux user, learning shell script is simply a boon. This sets you apart from those who just use Linux, then those who know how to use Linux!

To know more about shell and scripting, you may visit here.

If you have any query or idea to improve this post, please feel free to write in the comments section below.

Thanks for reading!

© The Geeky Way. Built using Pelican. Theme by Giulio Fidente on github.

Disclaimer Privacy policy