Home Scripting Introduction Basics  Redirect Cut/Paste Quoting Regular Expressions Sed Awk Scripts Books

Contents

Introduction

In this section we study the shell scripting language. We have seen how we can place commands in a shell script and run the script. The shell language offers much more including but not limited to decisions, loops and functions.

Running scripts

Let's create a simple script. We are going to place the "date" command in the script.


File: run1.sh
date We give the file execute permissions $ chmod 777 run1.sh Sometimes running on cygwin will give the following error. $ ./run1.sh ./run1.sh: line 1: $'date\r': command not found That's because the end of line character is different on windows than unix. $ dos2unix run1.sh dos2unix: converting file run1.sh to Unix format. $ ./run1.sh Thu, Apr 4, 2024 6:36:41 PM Let's try another script called "run2.sh" .
File: run2.sh
var1="Test" echo $var1 $ ./run2.sh Test The value of the variable is printed out. However printing the value of the variable from the prompt does not produce anything. $ echo $var1 The reason is when we run the shell script a child process got created and so the variable is specific to that child process and it's environment. The main shell is the parent process and it's environment gets passed to the child process but not vice-versa. We can run the script in the main shell. It's as if we are typing the commands at the prompt. There are 2 ways to do this. One way is using the "source" command. $ source ./run2.sh Test $ echo $var1 Test The other is using the "." before running the script. $ . ./run2.sh Test $ echo $var1 Test We can use this for environment variables also. To see what shell is being used we can run the below command. $ ps -p $$ PID PPID PGID WINPID TTY UID STIME COMMAND 1801 1 1801 11380 cons0 197609 Mar 24 /usr/bin/bash We can also see the current shells on our system. $ cat /etc/shells /bin/sh /bin/ash /bin/bash /bin/dash /bin/mksh /bin/pdksh ... We can tell the system what shell to use to execute our script with something called "shebang"
File: run3.sh
#!/bin/sh echo "Hello, ${USER}" $ ./run3.sh Hello, Deller The line "#!/bin/sh" at the top of the script tells the system to use the Bourne shell ( sh ) to execute this script. export We can use the "export" command to make a variable available to the child process. The environment variables are available to a child process but not custom variables.
File: run4.sh
$ var2="Test" $ chmod 777 run4.sh Nothing is printed out $ ./run4.sh $ export var2 $ ./run4.sh Test After exporting the script is able to access the value . We can also run another script from a script.
File: run5.sh
var3="run5_var" ./run5_1.sh
File: run5_1.sh
File: "run5_1.sh" echo "var3 value is $var3" $ ./run5.sh var3 value is The child script "run5_1.sh" is not able to see the value of "var3". We need to export it. File: "run5.sh" var3="run5_var" export var3 ./run5_1.sh $ ./run5.sh var3 value is run5_var From the same folder we can also run the script using the shell. $ bash run5.sh var3 value is run5_var In the above we are running the "run5.sh" using the "bash" shell. We can also run it using the "Bourne" shell. $ sh run5.sh var3 value is run5_var

Variables

A variable can hold a value such as a number or a string. A variable can be used without declaring a type. The shell will deduce the type. This is the main difference between a scripting and a high-level language.

A shell variable can only consis of letters, numbers and the underscore character. Also it cannot start with a number. The following script file illustrates some valid and invalid variable names.


File: valid1.sh
#Starts with a number 2VAR1="Value" #Has the invaild character "!" V!AR1="Value" #Has the invaild character "-" VAR1-VAR2="Value" VAR1="Value" _VAR1="Value" VAR_1="Value" $ ./valid1.sh ./valid1.sh: line 2: 2VAR1=Value: command not found ./valid1.sh: line 4: V!AR1=Value: command not found ./valid1.sh: line 6: VAR1-VAR2=Value: command not found

Strings

We can assign strings to variables.


File: str1.sh
VAR1="Value" echo $VAR1 #Below does not work because #shell interprets it as a variable #$VAR1Something echo $VAR1Something else #We need to put curly brackets around the #variable name echo "${VAR1}Something else" #Can work without double quotes also echo ${VAR1}Something else $ ./str1.sh Value else Something else Something else We can concatenate 2 strings by writing them next to each other. $ var1="dog""cat" $ echo $var1 dogcat Placing a space between the words. $ var1="dog"" ""cat" $ echo $var1 dog cat var1="dog" "cat" echo $var1 Exercise 1) Complete the "TO DO" so that "var3" contains the string "dogcat" var1=dog var2=cat var3=TO DO 2) Capture the output of the commands "date" and "whoami" in a single variable. If the user name is "user" then the output may look like: $ echo $var3 Fri, Mar 19, 2021 10:10:35 PM user Solutions 1) var1=dog var2=cat var3=${var1}${var2} 2) var3=`date` var3=${var3}${USER} echo $var3 Output: $ echo $var3 Sat, Apr 6, 2024 9:42:14 AMDeller

Numbers

We can perform operations with numbers in our scripts.
Some of the arithmetic operations are:

+ Addition
- Subtraction
/ Division
* Multiplication
% Modulus

There are several different ways to write the syntax
to perform these operations.


sum=`expr $num1 + $num2`
Using the back quote and expr command.

sum=$(expr $num1 + $num2)
Using single bracket.

sum=$(($num1 + $num2))
Using double bracket.



File: num1.sh
#!/usr/bin/bash NUM1=20 NUM2=41 # Addition SUM=$(( NUM1 + NUM2 )) echo "The sum is: $SUM" SUM=`expr $NUM1 + $NUM2` echo "The sum is: $SUM" SUM=$(expr $NUM1 + $NUM2) echo "The sum is: $SUM" # Subtraction DIFFERENCE=$(( NUM2 - NUM1 )) echo "The difference is: $DIFFERENCE" PRODUCT=$(( NUM1 * NUM2 )) echo "The product is: $PRODUCT" QUOTIENT=$(( NUM2 / NUM1 )) echo "The quotient is: $QUOTIENT" MODULUS=$(( NUM2 % NUM1 )) echo "When the two numbers are divided, the remainder is: $MODULUS" Output: $ ./num1.sh The sum is: 61 The sum is: 61 The sum is: 61 The difference is: 21 The product is: 820 The quotient is: 2 When the two numbers are divided, the remainder is: 1 The shell will determine the type for the variable. If we do arithmetic operations with a string then that variable will be deduced as zero.
File: num2.sh
#!/usr/bin/bash NUM1=20 NUM2="Test" # Addition SUM=$(( NUM1 + NUM2 )) echo "The sum is: $SUM" Output: $ ./num2.sh The sum is: 20 The shell does not have inherent capability to do floating point arithmetic.
File: num3.sh
#!/usr/bin/bash NUM1=12.5 NUM2=6.0 echo $(( NUM1 + NUM2 )) $ ./num3.sh ./num3.sh: line 5: 12.5: syntax error: invalid arithmetic operator (error token is ".5") We need to use the basic calculator(bc). This is a command line utility.
File: num4.sh
#!/usr/bin/bash NUM1=12.5 NUM2=6.0 echo "$NUM1 + $NUM2" | bc $ ./num4.sh 18.5

Exit Status

The string "$?" is the exit status of the last command.
File: ""

File: exit1.sh str1="Fred and ed go to school." echo $str1 | grep "ed" echo $? #finds a match so prints 0 echo $str1 | grep "Ed" echo $? #does not find a match so prints 1 echo $str1 | grep "Mary" echo $? #does not find a match so prints 1 Output: $ ./exit1.sh Fred and ed go to school. 0 1 1 The "$?" signifies the exit status. It will print 0 if the previous command succeeded and 1 if the previous command failed. In the previous example when grep finds a match "0" is returned. If we are writing our own scripts then we need to explicitly state the exit value. By convention, the exit value of a script is 0 if a program succeeded and 1 if the program failed. The string "$#" denotes the number of arguments passed to the shell script.
File: exit2.sh
echo $# if [ $# -ne 1 ] then echo "Wrong number of Arguments" exit 1 else exit 0 fi In the above we are checking if the number of arguments is equal to 1. We will explain the "if" statement in the next section. $ ./exit2.sh 0 Wrong number of Arguments Now when we print the exit status it prints 1 because our script returned 1. $ echo $? 1 We pass an argument to the script $ ./exit2.sh 100 1 Now the exit status is 0 . $ echo $? 0

Decisions

If Condition


File: if1.sh
var1=10 if [ $var1 -eq 10 ] then echo "Inside the if condition" fi $ ./if1.sh Inside the if condition The if condition involves an expression inside the square brackets. [ $var1 -eq 10 ] The "then" part can also be written after the if condition. var1=10 if [ $var1 -eq 10 ] ; then echo "Inside the if condition" fi If we write "then" after the "]" then we have to use a semi colon. We can also choose to replace the square brackets with the test condition.
File: if2.sh
var1=10 if test $var1 -eq 10 then echo "Inside the if condition" fi What sort of conditions can we place in if. The below sections show the conditions for "String" , "Files", "Numbers" . Strings -n String1 - the length of the String1 variable is nonzero -z String1 - the length of the String1 variable is 0 (zero) String1 = String2 - String1 and String2 variables are identical String1 != String2 - String1 and String2 variables are not identical String1 - true if String1 variable is not a null string
File: if3.sh
VAR1="Something" if [ -n $VAR1 ] then echo "VAR1 length is non zero" fi VAR2="" if [ -z $VAR2 ] then echo "VAR2 length is zero" fi VAR3="Martin" VAR4="Martin" VAR5="Luther" if [ $VAR3=$VAR4 ] then echo "VAR3 is equal to VAR4" fi if [ $VAR3!=$VAR5 ] then echo "VAR3 is not equal to VAR5" fi VAR6="King" if [ $VAR6 ] then echo "VAR6 is not empty" fi VAR7="" if [ $VAR7 ] then echo "VAR7 is not empty" fi Numbers -eq Integer1 -eq Integer2 - Integer1 and Integer2 variables are algebraically equal -ne - not equal -gt - greater than -ge - greater or equal -lt - less than -le - less or equal
File: if4.sh
#!/usr/bin/bash NUM1=20 NUM2=41 if [ $NUM1 -eq 20 ] then echo "NUM1 is 20" fi if [ $NUM1 == 20 ] then echo "NUM1 is 20" fi #Square brackets can be used as #either single square brackets or # double square brackets. if [ $NUM1 -le $NUM2 ] then echo "NUM1 is less than NUM2" fi if [[ $NUM1 -le $NUM2 ]] then echo "NUM1 is less than NUM2" fi Output: $ ./if4.sh NUM1 is 20 NUM1 is 20 NUM1 is less than NUM2 NUM1 is less than NUM2 Files -e FileName - FileName exists -b Filename - Returns a True exit value if the specified FileName exists and is a block special file -c FileName - FileName is a character special file -d FileName - FileName is a directory -f FileName - FileName is a regular file -g FileName - FileName's Set Group ID bit is set -h FileName - FileName is a symbolic link -k FileName - FileName's sticky bit is set -L FileName - FileName is a symbolic link -p FileName - FileName is a named pipe (FIFO) -r FileName - FileName is readable by the current process -s FileName - FileName has a size greater than 0 -t FileDescriptor - FileDescriptor is open and associated with a terminal -u FileName - FileName's Set User ID bit is set
File: if5.sh
#!/usr/bin/bash if [ -e "scripts11.html" ] then echo "scripts11.html file exists" fi if [ -d "scripts11.html" ] then echo "scripts11.html is a directory" fi if [ -d "1" ] then echo "1 is a directory" fi Output: $ ./if5.sh scripts11.html file exists 1 is a directory Exercise: Write a shell script "dir1.sh" that takes an argument and determines if that if the argument is a folder or not. ./dir1.sh folder1 "folder1" is a folder. ./dir1.sh file1 "file1" is not a folder.