Shell Scripting
Shell helps user to talk with them to kernel because the kernel code is implemented in C language but the user doesn’t know about C language. Hence, they are providing shell commands in the shell layer.
If you want to give many instructions to kernel at a time then you need to write those instructions or commands in one by one line and you need to save the file using .sh extension. The execution of commands from one by one line is called “Shell Script”.
Steven Bourne developed the Shell Script in path /bin/sh. The shell .sh was widely used earlier. But we need some improvements on .sh shell, so we have introduces the improved version called bash (/bin/bash). Bash stands for Bourne Again Shell.
There are different types of shell like csh (Korn Shell), ksh, zsh (Z Shell), fish (Interactive Shell).
How to write Shell Script:-
Create a file
ubuntu@ip-172-31-25-118:~/scripts$ vim scriptfile.sh
Once you create a file then you need to give write permission to yourself (user)
ubuntu@ip-172-31-25-118:~/scripts$ chmod 755 scriptfile.sh
Then you can run the script using 2 ways. One way is you can use “./<filename.sh> and another way is to use “bash filename.sh
Eg 1:
echo "Bilal: Hello Dosto, how are you?" echo "Audience: We are fine"
echo "Bilal: Today what date is it?" date
echo "I was waiting for all of you since" uptime
#echo "Now I am creating a directory" #mkdir ajit
Output: As you know this directory is already exist. If you run this command it will get the output → Initially when I run the command I have created the directory, hence we got this error.
mkdir: cannot create directory ‘ajit’: File exists
echo "There is one more person who will teach you"
User can input using read command. The word name_of_the_person acts as a variable
read name_of_the_person
#You can access the variable using $variable_name
echo "The person name is $name_of_the_person"
#An argument is a input which is given at the time of running a script.
echo "Your first argument will be $1" echo "The total no of arguments is $#"
#What things you have passed in argument
echo "I have passed this many things in argument: $@"
Output:
ubuntu@ip-172-31-25-118:~/scripts$ ./scriptfile.sh john rahul karishma → Here john, rahul and kairshma considered as an arguments
Bilal: Hello Dosto, how are you?
Audience: We are fine
Bilal: Today what date is it?
Sun Sep 15 07:22:37 UTC 2024
I was waiting for all of you since
07:22:37 up 7 min, 1 user, load average: 0.00, 0.00, 0.00
There is one more person who will teach you
sharukh → user will type input
The person name is sharukh
Your first argument will be john
The total no of arguments is 3
I have passed this many things in argument: john rahul karishma
Eg 2: It checks the vote eligibility based on age.
echo "Enter your age"
read age
if [ $age -le 17 ]; then
echo "You are not eligible for vote"
else
echo "Now you can vote"
fi
Output:
ubuntu@ip-172-31-25-118:~/scripts$ ./if_condition.sh
Enter your age
15
You are not eligible for vote
ubuntu@ip-172-31-25-118:~/scripts$ ./if_condition.sh
Enter your age
18
Now you can vote
Eg 3: It checks for a minimum number of arguments and print certain number of arguments.
#!/bin/bash
#Check if there are enough arguments
if [ "$#" -le 4 ]; then
echo "Please provide atleast 5 arguments"
exit 1 #It exit the program
fi
#Loop through arguments from 2nd to 5th array
for i in {2..5}; do
#Print the argument as per position $i echo "Argument $i: ${!i}" → access and print the value of a variable whose name is stored in another variable.
done
Output:
ubuntu@ip-172-31-25-118:~/scripts$ ./for_loop_example.sh 10 20 30 40
Please provide atleast 5 arguments
ubuntu@ip-172-31-25-118:~/scripts$ ./for_loop_example.sh 10 20 30 40 50 Arugement 2: 20
Arugement 3: 30
Arugement 4: 40
Arugement 5: 50
Eg: 4 It prompts the user for input to decide whether to proceed with a certain action.
#!/bin/bash
echo "Do you want to continue (yes/no)"
read user_input
if [ $user_input == "yes" ]; then
echo "You can proceed now"
elif [ $user_input == "no" ]; then
echo "You will not proceed"
else
echo "Default valid input"
fi
Output:
Do you want to continue (yes/no) yes
You can proceed now
ubuntu@ip-172-31-25-118:~/scripts$ ./boolean_condition.sh
Do you want to continue (yes/no) no
You will not proceed
ubuntu@ip-172-31-25-118:~/scripts$ ./boolean_condition.sh
Do you want to continue (yes/no) hbfh
Default valid input
Eg 5: If you want to do same task repeatedly then use for loop
#!/bin/bash
#Creating multi line comments
<< task
Create a custom directory with given range of arguments
task
echo "Enter the root directory"
read root_dir
mkdir $root_dir && cd $root_dir
echo "Enter the custom directory"
read custom_dir
for (( i=$1; i<=$2; i++ )); do
mkdir "${custom_dir}${i}"
done
Output:
ubuntu@ip-172-31-25-118:~/scripts$ ./create_custom_directories.sh 1 6
Enter the root directory
devops
Enter the custom directory
batch
ubuntu@ip-172-31-25-118:~/scripts$ cd devops/
ubuntu@ip-172-31-25-118:~/scripts/devops$ ls
batch1 batch2 batch3 batch4 batch5 batch6
Eg 6: I need to take a backup for my scripts directory and need to store in backup directory which I have created
ubuntu@ip-172-31-25-118:~/scripts$ ls
backup.sh
ubuntu@ip-172-31-25-118:~$ ls
backups
Source Code:
<< comment
This script will take the backup from source to target
comment
src_dir="/home/ubuntu/scripts/"
target_dir="/home/ubuntu/backups"
<< backups
needs to be compress
backups
#gz is an advance zipping mechanism, tar is a utility or a tool for zipping
backup_filename="backup_$(date +%Y-%m-%d-%H-%M-%S).tar.gz"
echo $backup_filename
echo "Backup is started"
echo "Backup will be stored at $backup_filename"
Now we are compressing it
#tar -czvf <dest> <src>
# -c used for compress, -x used to decompress or extract, -v is used to show the information visually, -f specifies the filename, z is used for gunzip
tar -czvf "${target_dir}/${backup_filename}" "$src_dir"
echo "Backup is completed"
Output:
ubuntu@ip-172-31-25-118:~/backups$ ls
backup_2024-09-22-05-37-50.tar.gz
If you want to extract the tar.gz files
ubuntu@ip-172-31-25-118:~/backups$ tar -xzvf backup_2024-09-22-05-37-50.tar.gz
home/ubuntu/scripts/ home/ubuntu/scripts/backup.sh home/ubuntu/scripts/create_custom_directories.sh home/ubuntu/scripts/for_loop_example.sh home/ubuntu/scripts/.backup.sh.swp home/ubuntu/scripts/devops/ home/ubuntu/scripts/devops/batch3/ home/ubuntu/scripts/devops/batch2/ home/ubuntu/scripts/devops/batch1/ home/ubuntu/scripts/devops/batch4/ home/ubuntu/scripts/devops/batch6/ home/ubuntu/scripts/devops/batch5/ home/ubuntu/scripts/boolean_condition.sh home/ubuntu/scripts/scriptfile.sh home/ubuntu/scripts/.backup.sh.swo home/ubuntu/scripts/ajit/ home/ubuntu/scripts/if_condition.sh
ubuntu@ip-172-31-25-118:~/backups$ ls
backup_2024-09-22-05-37-50.tar.gz home
Eg 7: I don’t want to run the script manually. I need to run it automatically in a scheduled time. Then we will use CRON. CRON is known as scheduled task.
I want to run the script in every hour. I’ll edit the crontab file of that script. Inside crontab, there will be a cron jobs.
ubuntu@ip-172-31-25-118:~/backups$ crontab -e
You can open a file using vim editor
ubuntu@ip-172-31-25-118:~/backups$ crontab -e
no crontab for ubuntu - using an empty one
Select an editor. To change later, run 'select-editor'.
/bin/nano <---- easiest/usr/bin/vim.basic/usr/bin/vim.tiny/bin/ed
Choose 1-4 [1]: 2
At every 2nd minute I want to take a backup
Search “cron guru” in google
Add this line in crontab file
/2 * bash /hbackup.shome/ubuntu/scripts/
It refreshes every 2 seconds which runs by ls command
ubuntu@ip-172-31-25-118:~/backups$ watch -n 2 ls
Functions:-
Eg:
check_even_odd () { → Creation of Function
echo "Enter number"
read number
if [ $(($number%2)) -eq 0 ];
then
echo "$number is even"
else
echo "$number is odd"
fi
}
check_even_odd → Function Calling
Output:
ubuntu@ip-172-31-25-118:~/scripts$ ./even_odd.sh Enter number
7
7 is odd
ubuntu@ip-172-31-25-118:~/scripts$ ./even_odd.sh Enter number
8
8 is even
Eg 8: Create the users and display their user name
Code:
#Define the users to be created
users=("Asad" "Gulnaz")
#user is a variable that hold the current value of item in the iteration
#$users[@] refers to elemenets of the arrray having it's own variable named users. for user in "${users[@]}"; do
#Check whether user is already there in system
#The id command is used to get the information about the user like it's user ID, group ID and at which group they belongs
#&>/dev/null is a special file that discards all the output of id command
if id "$user" &>/dev/null; then
echo "User $user is already exist"
else
sudo useradd "$user"
echo "Created user: $user"
fi
done
#Now list the created users
echo "Current users" cut -d':' -f1 /etc/passwd | grep -E 'Asad|Gulnaz'
Output:
ubuntu@ip-172-31-25-118:~/scripts$ ./user_management.sh
Created user: Asad
Created user: Gulnaz
Current users Asad
Gulnaz
If I run the same code again, then here is the output
User Asad is already exist
User Gulnaz is already exist
Current users
Asad
Gulnaz
Eg 9: Create a script that changes the permissions of multiple files in a directory
ubuntu@ip-172-31-25-118:~/qa_configs$ ll
-rw-rw-r-- 1 root ubuntu 0 Oct 14 07:25 demo.txt
Code:
#!/bin/bash
echo "Enter the directory path"
read dir_path
echo "Enter the permissions (eg, 755)"
read permissions
sudo chmod -R $permissions $dir_path → -R stands for recursive
echo "Permissions changed successfully"
Output:-
Enter the directory path
/home/ubuntu/qa_configs
Enter the permissions (eg, 755)
700
Permissions changed successfully
-rwx------ 1 root ubuntu 0 Oct 14 07:25 demo.txt*
Eg 10: Create a script with comments explaining what the script does.
#!/bin/bash
#Prints the current date and time
echo "The current date and time is: $(date)"
#Prints a welcome message
echo "Welcome to the Shell Scripting Challenge"
#End of script
Output:
The current date and time is: Wed Oct 16 14:27:38 UTC 2024
Welcome to the Shell Scripting Challenge
Eg 11: Create a script that declares the variables and assign the values to them.
name="Bilal DevOps Student"
course="DevOps"
echo "My name is $name and I am participating in the $course challenge"
Output:
My name is Bilal DevOps Student and I am participating in the DevOps challenge
Eg 12: Add two numbers and display their sum
num1=5
num2=15
sum=$((num1+num2))
echo "The sum of $num1 and $num2 is $sum."
Output: The sum of 5 and 15 is 20.
Eg 13: Define three built in variables that display the relevant information.
echo "Current logged-in user: $USER"
echo "Home directory: $HOME"
echo "Current shell: $SHELL"
Output:
Current logged-in user: ubuntu
Home directory: /home/ubuntu
Current shell: /bin/bash
Eg 14: Create a directory if the command is successful, else it prints an error message.
#!/bin/bash
DIR="/root/day11/task1"
#Check if the directory exist. -d is a test operator used in shell scripting to check if a directory exist.
if [ -d "$DIR" ]; then
echo "Directory $DIR already exists"
else
#Attempt to create a directory
sudo mkdir -p $DIR
#Check exit status. $? -> define the status of last command failed. In this case, it checks the mkdir command
if [ $? -ne 0 ]; then
echo "Failed to create a directory $DIR"
else
echo "Directory $DIR created successfully"
fi
fi
Output:
ubuntu@ip-172-31-25-118:~/scripts/error_handling$ sudo ./check_exit_status.sh Directory /root/day11/task1 created successfully
If I run the same script again
ubuntu@ip-172-31-25-118:~/scripts/error_handling$ sudo ./check_exit_status.sh Directory /root/day11/task1 already exists
Eg 15: Now I have modified the code. I have created a file inside the parent directory.
#!/bin/bash
DIR="/root/day11/task1"
FILE="$DIR/myfile.txt"
#Check if the directory exist. -d is a test operator used in shell scripting to check if a directory exist.
if [ -d "$DIR" ]; then
echo "Directory $DIR already exists"
else
#Attempt to create a directory
sudo mkdir -p $DIR
#Check exit staus. $? -> define the status of last command failed. In this case, it checks the mkdir command
if [ $? -ne 0 ]; then echo "Failed to create a directory $DIR"
else
echo "Directory $DIR created successfully"
fi
fi
#Check if the file exist
if [ -f "$FILE" ]; then
echo "$FILE already exists"
else
echo "Creating file $FILE.."
#Create the file
sudo touch "$FILE"
#Check the exit status of the touch command
if [ $? -ne 0 ]; then
echo "Failed to create file $FILE"
else
echo "File $FILE created successfully"
fi
fi
This is my first output
ubuntu@ip-172-31-25-118:~/scripts/error_handling$ sudo ./check_exit_status.sh Directory /root/day11/task1 already exists Creating file /root/day11/task1/myfile.txt.. File /root/day11/task1/myfile.txt created successfully
When I run same script again
ubuntu@ip-172-31-25-118:~/scripts/error_handling$ sudo ./check_exit_status.sh Directory /root/day11/task1 already exists
/root/day11/task1/myfile.txt already exists
Eg 16: Create a temp file and set a trap to delete the file
Create a directory inside /root/task3
#!/bin/bash
#Create a temp file
temp_file="/root/task3/tempfile_$$.txt"
touch "$temp_file"
echo "Temporary file created at $temp_file";
echo "Script is running"
#Script automatically creates temporary file for 10 seconds
sleep 10
#Normal exit
echo "Script completed succesfully"
#Set up a trap to delete the temporary file on exit
trap 'echo "Deleting temporary file"; rm -f "$temp_file"' EXIT
Output:
ubuntu@ip-172-31-25-118:~/scripts/error_handling$ sudo bash delete_temp_file.sh
Temporary file created at /root/task3/tempfile_5035.txt
Script is running
Script completed succesfully
Deleting temporary file
The trap command is used to specify the command when the shell receives the certain signal or when certain events get occurred.
Eg 17: Write a script that reads a non-existent file and redirects the error message to a file called error.log
#!/bin/bash
#Attempt to read a non-existent file
#This command read a file called non_existent_file.txt, which does not exist.
# 2> error.log -> This redirects to a standard error to a file name called error.log. If the file does not exist, it will create it.
cat non_existent_file 2> error.log
#Notify user -> Informs the user that error message has redirected.
echo "Error messages redirected to error.log"
Output:
ubuntu@ip-172-31-25-118:~/scripts/error_handling$ ./redirecting_errors.sh
Error messages redirected to error.log
ubuntu@ip-172-31-25-118:~/scripts/error_handling$ cat error.log
cat: non_existent_file: No such file or directory