2.bash Scripting
2.bash Scripting
Bash Scripting
1.Introduction
This tutorial will give you a solid platform in how to create bash scripts and automate day to day
system admin tasks. Certainly, everything cannot be covered in this chapter but you will be
equipped with right amount of knowledge to make your own scripts and excel in it if you put in
your own efforts.
Bash scripting is used by many of the system admins and DevOps geeks to get things done quickly
and efficiently. There are so many automation tools in the market like Ansible, Puppet, Chef etc.
Which are way more sophisticated but sometimes to get things done quickly in Linux systems we
use Bash scripts. Also, scripting will make you understand what automation means and then you
can quickly grasp the features that is used in Configuration Management tools like Ansible or
puppet.
A Bash script is a plain text file which contains a series of commands. These commands are a
mixture of commands we would normally type ourselves on the command line (such as ls or cp for
example) and commands we could type on the command line but generally wouldn't (you'll
discover these over the next few pages). A crucial point to remember though is:
Anything you can run normally on the command line can be put into a script and it will do
exactly the same thing. Similarly, anything you can put into a script can also be run normally
on the command line and it will do exactly the same thing.
First Script
As we discussed earlier that script is a normal text file with commands in it.
We will open a file vi editor and add some commands in it.
It is convention to give files that are Bash scripts an extension of .sh (print.sh for example)
$ vi print.sh
•#!/bin/bash
Explanation:
Line 1 - #! is called as the SHEBANG character, it tells the script to interpret the rest of the lines with an
Interpreter /bin/bash. So, if we change that to /usr/bin/python then it tells the script to use python interpreter.
Line 3- Is the command echo which will print a message to the screen. You can type this command yourself on
the command line and it will behave exactly the same.
imran@DevOps:.../bash$ ./print.sh
imran@DevOps:.../bash$ ls -l
total 4
imran@DevOps:.../bash$ ls -l
total 4
imran@DevOps:.../bash$ ./print.sh
Hello World!
Without giving execute permission also we can run the script but then we provide a shell and ask it
to run all the command in the script on that shell.
Hello World!
A variable is a temporary store for a piece of information. There are two actions we may perform for variables:
imran@DevOps:.../bash$ VAR1=123
123
When we run a program on the command line you would be familiar with supplying arguments after it to control
its behaviour.
For instance we could run the command ls -l /tmp. -l and /tmp are both command line arguments to the command
ls.
We can do similar with our bash scripts. To do this we use the variables $1 to represent the first command line
argument, $2 to represent the second command line argument and so on. These are automatically set by the
system when we run our script so all we need to do is refer to them.
copyscript.sh
1. #!/bin/bash
3. cp $1 $2
6. ls -lh $2
total 0
Explanation:
Line 3 - run the command cp with the first command line argument as the source and the second
command line argument as the destination.
Line 6 - After the copy has completed, run the command ls for the destination just to verify it worked.
We have included the options l to show us extra information and h to make the size human readable so
we may verify it copied correctly.
There are a few other variables that the system sets for you to use as well.
intA=20
floatB=20.20
stringA="first_string"
DIR_PATH="/tmp"
echo
echo "#########################"
echo "#########################"
echo "#########################"
echo "#########################"
echo "#########################"
echo "#########################"
ls $DIR_PATH
Quotes
Storing a single word in a variable works fine without quotes, but if we want to store a sentence and
also want to store special characters like $,%,@ etc our normal variable assignment will not work.
imran@DevOps:.../bash$
When we enclose our content in quotes we are indicating to Bash that the contents should be considered as a
single item. You may use single quotes ( ' ) or double quotes ( " ).
Double quotes will allow you to do substitution (that is include variables within the setting of the value).
Hello World
More $myvar
imran@DevOps:.../bash$
Command Substitution
We have how to store a string/text into a variable but sometimes you want to store output of a
command to a variable. Like you may need to store of ls command output to a variable. For this we
use Command Substitution. There are two syntax for doing this.
1.
imran@DevOps:.../testcopy$ file=`ls`
2.
imran@DevOps:.../testcopy$ files=$(ls)
imran@DevOps:.../bash$ var1=foo
foo
imran@DevOps:.../bash$ vi script1.sh
#!/bin/bash
var2=foobar
echo $var1
echo $var2
imran@DevOps:.../bash$ ./script1.sh
.profile and .bashrc lives in the users home directory so they are accessible only for that user.
/etc/profile is for global access for all the variables.
For example:
imran@DevOps:~$ cat .bashrc | grep export
#export
GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
export EDITOR=vim
export name=Cassini
Summary
$1, $2, ...
The first, second, etc command line arguments to the script.
variable=value
To set a value for a variable. Remember, no spaces on either side of =
Quotes " '
Double will do variable substitution, single will not.
variable=$(command )
Save the output of a command into a variable
export var1
Make the variable var1 available to child processes.
Interactive Scripts
read var1
input.sh
1. #!/bin/bash
4. read varname
imran@DevOps:.../bashscripts$ ./input.sh
DevTestOps
Explanation:
Line 4 - Run the command read and save the users response into the variable varname
Line 5 – echo another message just to verify the read command worked. Note: I had to put a backslash ( \
) in front of the ' so that it was escaped.
You are able to alter the behaviour of read with a variety of command line options. (See the man page for read to
see all of them.) Two commonly used options however are -p which allows you to specify a prompt and -s which
makes the input silent. This can make it easy to ask for a username and password combination like the example
below:
#!/bin/bash
echo
So far we have looked at a single word as input. We can do more than that however.
multiinput.sh
#!/bin/bash
echo Enter your Name, Profession & Interests in same order seprated by a space?
imran@DevOps:.../bashscripts$ ./multiinput.sh
Enter your Name, Profession & Interests in same order seprated by a space?
Basic If Statements
If you use bash for scripting you will undoubtedly have to use conditions a lot. Based on a condition
you decide if you should execute some commands on the system or not.
A basic if statement effectively says, if a particular test is true, then perform a given set of actions. If it is not true
then don't perform those actions. If follows the format below:
if [ <some test> ]
then
<commands>
fi
Anything between then and fi (if backwards) will be executed only if the test (between the square brackets) is
true.
if_example.sh
#!/bin/bash
# Basic if statement
if [ $1 -gt 100 ]
then
pwd
date
Explanation:
Line 3 - Let's see if the first command line argument is greater than 100
Line 5 and 6 - Will only get run if the test on line 4 returns true. You can have as many commands here
as you like.
Line 7 - fi signals the end of the if statement. All commands after this will be run as normal.
Line 8 - Because this command is outside the if statement it will be run regardless of the outcome of the
if statement.
/tmp/bash
intA=20
intB=30
if [ intA==intB ];
then
else
fi
if [ -f hosts ];
then
else
fi
Test
The square brackets ( [ ] ) in the if statement above are actually a reference to the command test. This means that
all of the operators that test allows may be used here as well. Look up the man page for test to see all of the
possible operators (there are quite a few) but some of the more common ones are listed below.
Operator Description
! EXPRESSION The EXPRESSION is false.
-n STRING The length of STRING is greater than zero.
-z STRING The length of STRING is zero (ie it is empty).
STRING1 = STRING2 STRING1 is equal to STRING2
STRING1 != STRING2 STRING1 is not equal to STRING2
INTEGER1 -eq INTEGER2 INTEGER1 is numerically equal to INTEGER2
INTEGER1 -gt INTEGER2 INTEGER1 is numerically greater than INTEGER2
INTEGER1 -lt INTEGER2 INTEGER1 is numerically less than INTEGER2
-d FILE FILE exists and is a directory.
-e FILE FILE exists.
-r FILE FILE exists and the read permission is granted.
-s FILE FILE exists and it's size is greater than zero (ie. it is not empty).
-w FILE FILE exists and the write permission is granted.
-x FILE FILE exists and the execute permission is granted.
= is slightly different to -eq. [ 001 = 1 ] will return false as = does a string comparison (ie. character for
character the same) whereas -eq does a numerical comparison meaning [ 001 -eq 1 ] will return true.
When we refer to FILE above we are actually meaning a path. Remember that a path may be absolute or
relative and may refer to a file or a directory.
Because [ 5 is just a reference to the command test we may experiment and trouble shoot with test on the
command line to make sure our understanding of its behaviour is correct.
Loops allow us to take a series of commands and keep re-running them until a particular situation is reached. They
are useful for automating repetitive tasks.
For Loop
A ‘for loop’ is a bash programming language statement which allows code to be repeatedly executed. A for loop is
classified as an iteration statement i.e. it is the repetition of a process within a bash script. For example, you can
run Linux command or task 5 times or read and process list of files using a for loop. A for loop can be used at a
shell prompt or within a shell script itself.
The for loop will take each item in the list (in order, one after the other), assign that item as the
value of the variable var, execute the commands between do and done then go back to the top, grab
the next item in the list and repeat over.
do
echo "$Variable"
done
do
echo $a
done
do
cat "$Variable"
done
do
cat "$Output"
done
While loop
The bash while loop is a control flow statement that allows code or commands to be executed repeatedly
based on a given condition. For example, run echo command 5 times or read text file line by line or evaluate
the options passed on the command line for a script.
syntax
The syntax is as follows:
while [ condition ]
do
command1
command2
command3
done
command1 to command3 will be executed repeatedly till condition is true. The argument for a while loop
can be any Boolean expression. Infinite loops occur when the conditional never evaluates to false. For
example, following while loop will print welcome 5 times on screen:
a=1
while [ $a -le 5 ]
do
x=$(( $x + 1 ))
done
#!/bin/bash
• for i in `cat hosts`;do
• echo "Printing list of hosts."
• echo $i
• done
Explanation
Line 1. For command substitution we are using backticks `` . It’s different from single quote ‘’
`cat hosts` will return the content of hosts file line by line, which will be stored in variable “i”.
The ANSI/VT100 terminals and terminal emulators are not just able to display black and white text
; they can display colours and formatted texts thanks to escape sequences. Those sequences are
composed of the Escape character (often represented by ”^[” or ”<Esc>”) followed by some other
characters: ”<Esc>[FormatCodem”.
In Bash, the <Esc> character can be obtained with the following syntaxes:
❖ \e
❖ \033
❖ \x1B
#!/bin/bash
#COLORS
# Reset
# Regular Colors
Red='\033[0;31m' # Red
Green='\033[0;32m' # Green
Yellow='\033[0;33m' # Yellow
Purple='\033[0;35m' # Purple
Cyan='\033[0;36m' # Cyan
## Install AMP
# Permissions
# Enabling Mod Rewrite, required for WordPress permalinks and .htaccess files
# Restart Apache
In this example, we will create a file and mention the name of log files that needs to be backup up
with tar command. Before taking backup, our script will also tell us if the log file exists or not. It
will skip the backup procedure if the file does not exists. After all there is no point running backup
command if the log file does not exist.
Create directory for storing log files
$ mkdir -p /tmp/scripts/logs
You can choose to put some content in the log files, touch will just create empty files.
$ cd /tmp/scripts
Create a file where you place the name of the files that you want to backup.
$ vi backup_files.txt
apache.log
mysql.log
nagios.log
ansible.log
chef.log
There is one extra filename chef.log which is not present in our logs directory /tmp/scripts/logs.
We will see how we will handle it in our script
$ vi backup.sh
#!/bin/bash
LOG_DIR='/tmp/scripts/logs'
BACKUP_DIR='/tmp/scripts/logs_backup'
mkdir -p $BACKUP_DIR
if [ -f $LOG_DIR/$i ];
then
cp $LOG_DIR/$i $BACKUP_DIR
else
fi
done
echo
echo
echo
echo
mysqldump command is used to take the db dump for mysql. In the script, we’re
taking the dbdump and sending it to a target directory in a zipped format. We
are also removing 8 days old dbbackup file by using find command. This process
is called a purging or purging old backup/log files.
#!/bin/sh
TIME_NOW="$(date +'%d_%m_%Y_%H_%M_%S')"
BACKUPFILE="db_backup_$TIME_NOW".gz
BACKUP_DIR="/opt/db_backup_dir"
PATHOFBACKUPFILE="$BACKUP_DIR/$BACKUPFILE"
LOG_FILE="$BACKUP_DIR/"backup_log_"$(date +'%Y_%m')".txt
exit 0
For this exercise, we will choose to run “yum install httpd” on three nodes.
Assumptions:
1. Three centos VMs
2. VMs have internet connection to download and install software.
3. All VMs have same username to connect.
We will create a file named “hosts-dev” and add ip address of all three nodes in that.
$ vi hosts-dev
192.168.2.5
192.168.2.6
192.168.2.7
We will write a script which will read the ip address from the hosts-dev file, do ssh to all of them one by one and run
yum install httpd command over ssh.
$ vi install.sh
#!/bin/bash
do
done
Line 2: for loop will run over the content on hosts-dev file one by one, which are the ip addresses of the VMs. Notice
we have used backticks `` and not single quotes ‘’ to read the hosts-dev file ( `cat hosts-dev` ).
Every time it logs into the vm’s/sever/nodes it will ask you a password, it will be painful if you have lot of servers that
you manage to enter password manually. In the next section, we will deal with this issue by doing ssh key exchange.
The above script will install httpd package on all the three nodes but will also
ask you password everytime it does a ssh login. To avoid this we can do key
based login which is discussed in next chapter.
$ ssh-keygen -t rsa
You can press enter here, saving the file to the user home (in this case, my example user is called
demo).
It's up to you whether you want to use a passphrase. Entering a passphrase does have its benefits:
the security of a key, no matter how encrypted, still depends on the fact that it is not visible to
anyone else. Should a passphrase-protected private key fall into an unauthorized user’s possession,
they will be unable to log in to its associated accounts until they figure out the passphrase, buying
the hacked user some extra time. The only downside, of course, to having a passphrase, is then
having to type it in each time you use the Key Pair.
$ ssh-keygen -t rsa
4a:dd:0a:c6:35:4e:3f:ed:27:38:8c:74:44:4d:93:67 demo@a
| .oo. |
| . o.E |
| + . o |
| . = = . |
| = S = . |
| o + = + |
| . o + o . |
| . o |
| |
+-----------------+
The public key is now located in /home/demo/.ssh/id_rsa.pub The private key (identification) is now located in
/home/demo/.ssh/id_rsa
You can copy the public key into the new machine's authorized keys file with the ssh-copy-id
command. Make sure to replace the example username and IP address below.
$ ssh-copy-id [email protected]
[email protected]'s password:
Now try logging into the machine, with "ssh '[email protected]'", and check in:
~/.ssh/authorized_keys
to make sure we haven't added extra keys that you weren't expecting.
Now you can go ahead and log into [email protected] and you will not be prompted for a
password. However, if you set a passphrase, you will be asked to enter the passphrase at that time
(and whenever else you log in in the future).
Exercise:
- Create 4 centos vm’s. One among these four vm one will be the automation box from where we
will run our scripts.
- Do SSH key exchange from automation box to rest of the three vm’s.
- Write a script which will install Apache & mysql server, start and enable both services and check
status of apache, mysql & iptables. Execute this script from automation box for all three vm and
validate.
4. Check hard disk free space and alerts if its running low on disk space.
#!/bin/bash
echo ""
sudo updatedb
if [ $? == 0 ]; then
else
fi
echo ""
echo ""
echo ""
# For CentOS 7
# Enable either option (comment out the other line), but not both.
# Option 1: Delete files without prompting for confirmation. Assumes GNU version
of find.
# Option 2: Ask for confirmation before deleting files. More portable across
systems.
echo ""
THRESHOLD=10
USAGE=${PERCENTAGE%?}
fi
if [ -f /var/run/nginx.pid ]
then
else
fi
# crontab -e
* * * * * /opt/scripts/nginstart.sh
if [ $? -eq 0 ]
then
sleep 3
sleep 5
else
sleep 3
sleep 5
fi
Summary:
• Scripting is required for doing system tasks automatically without manual intervention.
• Bash scripting is used by Linux System Admin for ages to automation Linux tasks.
• Variables, Condition's, loops etc are important aspects of scripting language which helps us
automate complex tasks.
• Try all the sample and real-time use case scripts to get hold of system automation.
Python should be your next choice of scripting language which is easier to read and write and is
also versatile, it can be used to automate tasks on windows platform and cloud services also.
Python has very less syntax for example check below “if condition” of bash vs python.
Bash way
#!/bin/bash
a=5
if [ $a -lt 10 ]
then
exit 1
fi
Python way
#!/usr/bin/python
a=10
if a < 10:
Both have their upsides and downside, you should make your choices wisely. But making choices
comes with experience and practice. There is no perfect way of doing anything.
Practice makes improvement.