DevOps 6.1: In and Out of Scripting
Everything and Everywhere Shell Scripting
How to declare ?
"$" it used to access the package
Example of Command line Arguments:
#! /bin/bash
# Check if the required number of arguments is provided
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <web_template_url>"
exit 1
fi
web_template_url="$1"
######## Install required packages ########
sudo yum install wget unzip httpd -y
######## Start and enable Apache HTTP server ########
sudo systemctl start httpd
sudo systemctl enable httpd
######## Create a temporary directory for web files ########
mkdir -p /tmp/webfiles
cd /tmp/webfiles
######## Download and extract a web template ########
wget "$web_template_url"
template_filename=$(basename "$web_template_url")
unzip "$template_filename"
######## Copy the extracted files to the web server's directory ########
sudo cp -r "$template_filename"/* /var/www/html/
######## Restart the Apache HTTP server to apply changes ########
sudo systemctl restart httpd
######## Clean up: Remove the temporary directory ########
rm -rf /tmp/webfiles
To run we use ./your_script.sh
https://www.tooplate.com/zip-templates/2136_kool_form_pack.zip
In the modified script, command line arguments are utilized to make the script more flexible and configurable. Let's break down the relevant parts related to command line arguments:
# Check if the required number of arguments is provided
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <web_template_url>"
exit 1
fi
web_template_url="$1"
$#
represents the number of command line arguments provided to the script.The
if
statement checks whether the number of arguments is not equal to 1. If it's not, the script displays a usage message and exits.$0
is a special variable representing the script's name itself, used in the usage message to indicate how to correctly use the script."$1"
is the first command line argument provided to the script, which is expected to be the URL of the web template.The provided URL is assigned to the variable
web_template_url
.
So, when you run the script, you should provide the web template URL as a command line argument. For example:
./your_script.sh https://www.tooplate.com/zip-templates/2136_kool_form_pack.zip
This allows you to reuse the same script with different web template URLs without modifying the script itself. It adds flexibility and makes the script more versatile for different use cases.
System Varibles:
In Bash scripting, system variables (also known as environment variables) are special variables that hold information about the environment in which the shell is running. These variables are set by the system and can be accessed by scripts to obtain information or modify behavior. Here are some commonly used system variables in Bash:
$HOME
: Represents the home directory of the user.echo "Home directory: $HOME"
$USER
and$LOGNAME
: Contain the username of the current user.echo "Username: $USER"
$PATH
: Contains a colon-separated list of directories in which the shell looks for executable files.echo "Path: $PATH"
$PWD
: Represents the current working directory.echo "Current working directory: $PWD"
$SHELL
: Indicates the path to the shell.echo "Shell path: $SHELL"
$TERM
: Specifies the terminal type.echo "Terminal type: $TERM"
$UID
and$EUID
: Contain the user ID and effective user ID, respectively.echo "User ID: $UID"
$HOSTNAME
: Represents the hostname of the machine.echo "Hostname: $HOSTNAME"
$RANDOM
: Generates a random integer between 0 and 32767.echo "Random number: $RANDOM"
$BASH_VERSION
: Displays the version of Bash.echo "Bash version: $BASH_VERSION"
These are just a few examples of system variables available in Bash. You can use the env
command to see a list of all environment variables in your current shell session. For example:
env
This will display a list of all environment variables along with their values.
Quotes in Bash Scripting:
In Bash scripting, quotes are used to define the boundaries of a string. There are three types of quotes in Bash: single quotes ('
), double quotes ("
), and backticks (``).
Single Quotes (
'
):- Enclosing characters in single quotes preserves the literal value of each character within the quotes. No variable substitution or command substitution occurs within single quotes.
echo 'This is a single-quoted string'
The output will be: This is a single-quoted string
Double Quotes (
"
):- Double quotes allow for variable substitution and command substitution. They also preserve the literal value of most characters within the quotes.
name="John"
echo "Hello, $name!"
The output will be: Hello, John!
Double quotes also allow for escaping certain characters to include them literally:
echo "This is a double-quoted string with a \"quote\" inside."
The output will be: This is a double-quoted string with a "quote" inside.
Backticks (``): (Command Subsitution)
- Backticks (grave accents) are used for command substitution. The command within backticks is executed, and its output is substituted in place.
current_date=`date`
echo "Current date is: $current_date"
The output will be something like: Current date is: Tue Nov 9 12:34:56 UTC 2023
Note: The use of backticks for command substitution is deprecated in favor of $()
syntax.
current_date=$(date)
echo "Current date is: $current_date"
This accomplishes the same result as the previous example.
In summary, quotes in Bash serve different purposes:
Single quotes (
'
) preserve the literal value of each character.Double quotes (
"
) allow variable and command substitution, but still preserve the literal value of most characters.Backticks (``) are used for command substitution, but
$()
is preferred for clarity and readability in modern Bash scripting.
Exporting Variables:
In Bash scripting, you can use the export
command to make a variable available to child processes, effectively turning it into an environment variable. Here are examples of exporting variables:
Example 1: Exporting a Variable
#!/bin/bash
# Define a variable
my_var="Hello, World!"
# Export the variable
export my_var
# Run a script that uses the exported variable
./another_script.sh
In this example, my_var
is defined and then exported. The variable will be accessible in another_script.sh
as an environment variable.
Example 2: Using Exported Variables in Another Script
Let's assume another_script.sh
looks like this:
#!/bin/bash
# Access the exported variable
echo "In another script: $my_var"
When you run the main script, it sets and exports my_var
, and then it executes another_script.sh
. The output of another_script.sh
will be:
In another script: Hello, World!
Example 3: Exporting Variables Directly
You can also export a variable in a single line without first defining it:
#!/bin/bash
# Export a variable directly
export another_var="This is another variable"
# Run a script that uses the exported variable
./yet_another_script.sh
In this case, another_var
is defined and exported in one step.
Example 4: Viewing Exported Variables
To see a list of all currently exported variables, you can use the export
command without any arguments:
export
This will display a list of all exported variables and their values.
Example 5: Exporting Variables for Subsequent Commands
Variables can be exported just for the duration of a single command using the VAR=value command
syntax:
#!/bin/bash
# Export variable for a single command
my_temp_var="Temporary value" another_var="Another temporary value" ./some_command.sh
In this case, some_command.sh
will have access to my_temp_var
and another_var
for the duration of its execution.
Remember that exported variables are inherited by child processes but not by parent processes. Any changes made in a child process will not affect the parent process.
These examples illustrate how to use the export
command to make variables available as environment variables for child processes.
Also if you want to make a variable permanent across sessions and for all scripts, you can add the export statement to your shell configuration file, such as ~/.bashrc
for Bash. Here's an example:
Open or create your
~/.bashrc
file using a text editor.vim ~/.bashrc
Add the following line to export a variable:
export my_permanent_var="This variable is permanent!"
You can replace "This variable is permanent!" with the actual value you want to assign.
Save the file and exit the text editor.
To apply the changes, either restart your terminal or run:
source ~/.bashrc
Now, the variable my_permanent_var
will be available in all new terminal sessions.
Here's an example script (use_permanent_var.sh
) that uses the exported variable:
#!/bin/bash
echo "Using the permanent variable: $my_permanent_var"
When you run this script, it will access the my_permanent_var
variable set in your ~/.bashrc
:
./use_permanent_var.sh
The export in ~/.bashrc
ensures that my_permanent_var
is available in every new Bash session. This makes the variable persistent across sessions and scripts, making it essentially "permanent" as long as you're using the same user account on the system.
For globally use the /etc/profile
it will ensure changes across users.
User Input in Scripts:
In Bash scripting, you can use the read
command to take user input. The read
command reads a line of text from the standard input (usually the keyboard) and assigns it to a variable. Here's an example:
#!/bin/bash
# Prompt the user for input
echo "Please enter your name:"
read user_name
# Display a greeting using the input
echo "Hello, $user_name! Welcome to the script."
Explanation:
echo "Please enter your name:"
: Displays a prompt asking the user to enter their name.read user_name
: Reads the user's input and assigns it to the variableuser_name
.echo "Hello, $user_name! Welcome to the script."
: Displays a greeting using the input provided by the user.
When you run this script, it will wait for you to enter your name, and then it will display a personalized greeting.
You can also use the -p
option with read
to combine the prompt and input statement and you can use -s
to suppress the password:
#!/bin/bash
# Read user input with a prompt
read -p "Please enter your name: " user_name
read -sp "please enter password: " password
# Display a greeting using the input
echo "Hello, $user_name! Welcome to the script."
Decision Making (if-else):
#!/bin/bash
# Prompt the user to enter a number
read -p "Enter a number: " NUM
echo
# Check if the entered number is greater than 100
if [ "$NUM" -gt 100 ]; then
# If the condition is true, execute the following commands
echo "We have entered the IF block."
sleep 3
echo "Your number is greater than 100."
echo
date
else
echo "Number is less than 100"
fi
# to mark the end of if and else block
# Regardless of the condition, this line will be executed
echo "Script execution"
elif:
#!/bin/bash
# Execute the command and store the result in the 'value' variable
value=$(ip addr show | grep -v LOOPBACK | grep -ic mtu)
# Check the value of 'value' variable using if-elif-else statements
if [ $value -eq 1 ]; then
# If there is exactly 1 active network interface, print the following message
echo "1 Active network interface found"
elif [ $value -gt 1 ]; then
# If there are more than 1 active network interfaces, print the following message
echo "Found multiple active interfaces."
else
# If there are no active network interfaces, print the following message
echo "No active interfaces found"
fi
Monitoring Script:
Let's right a script if a process is running if not we will start the process:
#!/bin/bash
date
ls /var/run/httpd/httpd.pid &> /dev/null
if [ $? -eq 0 ]
then
echo "Httpd process is running."
else
echo "Httpd process is not running"
echo "Starting the process"
sudo systemctl start httpd
if [ $? -eq 0 ]
then
echo "Process started successfully"
else
echo "Process Starting Failed, contact the admin"
fi
fi
Now, lets schedule this script to run automatically:
For that we will use crontab
:
Type crontab -e
:
# MM HH DOM mm DOW COMMAND
30 20 * * 1-5 COMMAND
* * * * * /opt/scripts/11_monit.sh &>> /var/log/monit_httpd.log
Loops:
#!/bin/bash
# Example 1: Loop through a range of numbers
echo "Example 1: Loop through a range of numbers"
for i in {1..5}
do
echo "Iteration $i"
done
# Example 2: Loop through an array of values
echo -e "\nExample 2: Loop through an array of values"
fruits=("Apple" "Banana" "Orange" "Grapes")
for fruit in "${fruits[@]}"
do
echo "Fruit: $fruit"
done
#!/bin/bash
# Initialize a counter
counter=1
# Example 1: Simple while loop
echo "Example 1: Simple while loop"
while [ $counter -le 5 ]
do
echo "Iteration $counter"
((counter++)) # Increment the counter
done
# Example 2: While loop with user input
echo -e "\nExample 2: While loop with user input"
echo "Enter a number (enter 0 to exit):"
read user_input
while [ $user_input -ne 0 ]
do
echo "You entered: $user_input"
echo "Enter another number (enter 0 to exit):"
read user_input
done
echo "End of the script"
Remote Command Execution:
Do vagrant up
and vagrant ssh scriptbox and do sudo -i
for the below file:
Vagrant.configure("2") do |config|
config.vm.define "scriptbox" do |scriptbox|
scriptbox.vm.box = "jacobw/fedora35-arm64"
scriptbox.vm.network "private_network", ip: "192.168.56.26"
scriptbox.vm.hostname = "scriptbox"
scriptbox.vm.provider "vmware_desktop" do |v|
v.allowlist_verified = true
v.ssh_info_public = true
v.gui = true
end
scriptbox.vm.provision "shell", inline: <<-SHELL
mv /etc/yum.repos.d/fedora-updates.repo /tmp/
mv /etc/yum.repos.d/fedora-updates-modular.repo /tmp/
yum clean all
yum update
systemctl stop firewalld
SHELL
end
config.vm.define "web01" do |web01|
web01.vm.box = "jacobw/fedora35-arm64"
web01.vm.network "private_network", ip: "192.168.56.27"
web01.vm.hostname = "web01"
web01.vm.provider "vmware_desktop" do |v|
v.allowlist_verified = true
v.ssh_info_public = true
v.gui = true
end
web01.vm.provision "shell", inline: <<-SHELL
mv /etc/yum.repos.d/fedora-updates.repo /tmp/
mv /etc/yum.repos.d/fedora-updates-modular.repo /tmp/
yum clean all
yum update
systemctl stop firewalld
SHELL
end
config.vm.define "web02" do |web02|
web02.vm.box = "jacobw/fedora35-arm64"
web02.vm.network "private_network", ip: "192.168.56.28"
web02.vm.hostname = "web02"
web02.vm.provider "vmware_desktop" do |v|
v.allowlist_verified = true
v.ssh_info_public = true
v.gui = true
end
web02.vm.provision "shell", inline: <<-SHELL
mv /etc/yum.repos.d/fedora-updates.repo /tmp/
mv /etc/yum.repos.d/fedora-updates-modular.repo /tmp/
yum clean all
yum update
systemctl stop firewalld
SHELL
end
config.vm.define "web03" do |web03|
web03.vm.box = "spox/ubuntu-arm"
web03.vm.box_version = "1.0.0"
web03.vm.network "private_network", ip: "192.168.56.29"
web03.vm.hostname = "web03"
web03.vm.provider "vmware_desktop" do |v|
v.allowlist_verified = true
v.ssh_info_public = true
v.gui = true
end
end
end
Add the IP address of web01, web02, and web03 to /etc/hosts
file in scriptbox.
ping the VMS from scriptbox to check.
Do ssh vagrant@web01
.
Then create a user using useradd devops
and give a password using passed devops
.
Add the devops to the sudoers file to give administrative access.
DO the same for web02 and web03 as well
So, above you can see how I executed a command on web01 by being scriptbox VM.
SSH Key Exchange:
In the above, it is always asking us for a password. So now we will do key based login which is a safer login.
So do ssh-keygen - The ssh-keygen
command is used to generate SSH key pairs. SSH keys are a pair of cryptographic keys that can be used to securely authenticate and communicate between two parties over an insecure network.
Then press enter for the following.
Then do ssh-copy-id devops@web01
- The ssh-copy-id
command is used to install your public key on a remote server, allowing you to log in without the need for a password.
Do the same for web02 and web03.
Now, you run commands without typing the password.
Conclusion:
In this comprehensive blog series, we embarked on a journey through the fundamentals of Bash scripting, covering a wide spectrum of topics. We started from the basics, including variable declaration, handling command line arguments, exploring system variables, understanding the significance of quotes, mastering the art of exporting variables, and seamlessly integrating user input.
We delved into the realm of control structures, unraveling the power of if-else statements and introducing the concepts of monitoring and looping. From system status checks to dynamic script execution, we crafted a robust foundation for automating tasks.
The grand finale of this series awaits in the next blog, where we will synthesize and apply our knowledge. The culmination will be a practical demonstration, incorporating all the elements we've learned. Brace yourselves for a hands-on experience that will serve as a launchpad for future endeavors in the world of Bash scripting. Stay tuned for the exciting conclusion to this enlightening series!