Introduction to Shell Programming

  • hello-world
  • permissions and #!
  • numerical expressions
  • add.sh
  • loops

Introduction

A shell script is file that contains the list of commands that you want to run in a sequence.

Let’s write a simple script.

hello-world.sh
echo "Hello, world!"

We can run this as:

$ bash hello-world.sh
Hello, world!

To run this script directly without invoking bash, we need do two things::

  1. First, we need to add a hash-bang directive in the first line of the script
  2. Make the script executable

Let’s add the #! line.

hello-world.sh
#! /bin/bash
echo "Hello, world!"

The #! line is the unix way to tell the system that execute this script using bash.

After adding that line, we still need to make the file executable. The file not executable unless we explicitly make it so. Let’s look at the output of ls -l.

$ ls -l hello-world.sh
-rw-rw-r-- 1 anand anand 34 Aug 19 09:07 hello-world.sh

The first column of the output indicates that the file has read, and write permissions.

We do add execute permissions by running chmod.

$ chmod +x hello-world.sh

$ ls -l hello-world.sh
-rwxrwxr-x 1 anand anand 34 Aug 19 09:07 hello-world.sh

Now the file is exutable. We can execute it using:

$ ./hello-world.sh
Hello, world!

Comments

In shell scripts, # is the comment prefix. Everything after that character is considered a comment.

hello-world.sh
#! /bin/bash
#
# This program prints a hello world message
#

echo "Hello, world!" # you can change the message here, if you wish

# end of program

Variables

Variables are placeholders to a value. Shell is weekly typed, so it doesn’t bother about datatypes like C.

You can define a variable just in a shell.

$ fruit=Apple

Please note that the $ at the beginning of the line is just prompt of the shell. That is not something that you type.

Note

Remember that shell is very sensitive to spaces. You can set a variable only as name=value.

If you put spaces around =, it will complain. So name = value is invalid.

$ x = 2
x: command not found

In this case shell interprets it as invoking command with name x with two arguments = and 2. Just like what happens when we call echo hello world.

To use the value of a variable, we prefix the variable name with $.

$ echo $fruit
Apple
$ echo I like $fruit
I like Apple

When we want to use a variable in a sentence, we typically enclose it double quotes. We’ll learn more about quotes in a latter section.

$ echo "I like $fruit!"
I like Apple!

Predefine variables

There are some variables already defined by the system. Variables like USER, HOME, SHELL, PATH etc. are usually defined for every session by the system.

$ echo $USER
anand
$ echo $HOME
/home/anand
$ echo $SHELL
/bin/bash
$ echo $PATH
/home/anand/.local/bin:/home/anand/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

The variable PATH is an interesting one. When you type a command, the system search for a file with that name in all the paths specified in the variable PATH. We’ll learn more about it later.

Integer expressions

Shell has a shorthand for working with integer values. Put an expression in `$((..))``.

$ x=3
$ y=4
$ echo "$((x + y))"
7
$ echo "$((x * y))"
12
$ echo "$((x ** y))"
81

Please note that this is limited only to integers, doesn’t work with floating point numbers.

If you want to work with floating point numbers, use tools like bc or awk.

Arguments

The command line arguments passed to a shell script are available in a special variable $*. You can use $# to find the number of arguments passed.

args.sh
#! /bin/bash
echo "You have given $# arguments"
echo "The arguments are $*"
echo "The first argument is $1"
echo "The second argument is $2"

Let’s make the script executable.

$ chmod +x args.sh
$ ls -l args.sh
-rwxrwxr-x 1 anand anand 113 Aug 19 07:53 args.sh

Let’s run it.

$ ./args.sh hello world
You have given 2 arguments
The first argument is hello
The second argument is world

If you give less than 2 arguments $2 will be empty. Shell is weakly typed, so it doesn’t complain.

$ ./args.sh hello
You have given 1 arguments
The first argument is hello
The second argument is

Now lets write a shell script add-two-numbers.sh that takes two numbers as command line arguments and prints their sum.

add-two-numbers.sh
#! /bin/bash
a=$1
b=$2
echo $((a+b))

Let’s make the script executable.

$ chmod +x add-two-numbers.sh

And run it.

$ ./add-two-numbers.sh 3 4
7

Problem: Write a program square.sh that takes a number as command-line argument and prints its square.

$ ./square.sh 5
25

Reading input

The read

hello.sh
read -p "What is your name? " name

echo "Hello $name! Nice to meet you!!"
$ bash hello.sh
What is your name? Anand
Hello Anand! Nice to meet you!!

Quotes

There are three kinds of quotes used in shell scripts.

single quotes

Single quotes preserve the literal value of the enclosed characters.

$ echo 'You owe me $10 & an apple!'
You owe me $10 & an apple!

Double quotes

Double quotes preserves the literal value of the enclosed characters, but expands the variables and allows escape characters.

$ echo "hello $USER, how are you doing?"
hello anand, how are you doing?

$ echo "2 * 3 = $((2 * 3))"
2 * 3 = 6

Question:

What would the output of the following command? Can you explain why it produces a different result than what is shown in the example above?

echo 2 * 3 = $((2 * 3))

Back Quotes

Shell allows putting a command in backquotes `...` and replaces it with the output of that command.

$ echo "The current date is `date`"
The current date is Monday 19 August 2024 08:48:39 AM IST

We can also use $(...) instead of `...`.

$ echo "The current date is $(date)"
The current date is Monday 19 August 2024 08:48:39 AM IST

The date command also supports custom formatting, we can use that to select that the day of the week.

$ echo "Today is a $(date +%A)"
Today is a Monday

We could also do the same thing using a variable.

$ day=$(date +%A)
$ echo "Today is a $day"
Today is a Monday

Loops

Shell provides two kinds of loops, for loop and while loop.

The for loop

square-args.sh
#! /bin/bash
#
# Program to compute square of all the numbers passed as command-line arguments
#
for n in $*
do
    echo $((n*n))
done
$ bash square-args.sh 1 2 3 4
1
4
9
16

We can use wildcards to list file.

$ for f in *.c; do figlet $f; done
  ___(_)_ __ ___| | ___   ___
 / __| | '__/ __| |/ _ \ / __|
| (__| | | | (__| |  __/| (__
 \___|_|_|  \___|_|\___(_)___|

     _             _     _
  __| | ___  _   _| |__ | | ___   ___
 / _` |/ _ \| | | | '_ \| |/ _ \ / __|
| (_| | (_) | |_| | |_) | |  __/| (__
 \__,_|\___/ \__,_|_.__/|_|\___(_)___|

If we want to loop over a sequence of numbers, we could make use of the seq command.

$ for n in `seq 5`; do echo "$((n*n))"; done
1
4
9
16
25

The while loop

The while loop is used with a condition and it continues to run until the condition is false. It is very handly to read data from stdin or a file.

squares.sh
#! /bin/bash

# read each line in the input input variable `n`
while read n
do
    echo $((n*n))
done
$ chmod +x squares.sh
$ seq 5 | ./squares.sh
1
4
9
16
25

Problem: Write a program sum.sh to read numbers from stdin and print their sum.

$ seq 5 | ./sum.sh
15
$ seq 5 | ./squares.sh | ./sum.sh
55

Summary

Shell Special variables

$*
All Arguments
$#
The numner of arguments
$0
The program name
$1, $2, $3, …
Individual arguments