Evolution of Bash, Writing and Executing Bash commands and Scripts
Hi, I am Malathi Boggavarapu working at Volvo Group and i live in Gothenburg, Sweden. I have been working on Java since several years and had vast experience and knowledge across various technologies.
In recent times i faced a situation where i need to write some bash scripts in my project that finally lead me to update my blog with Bash course. This course will guide you through various bash commands and also about writing bash scripts. This course addresses all the concepts with real time examples.
So let's get started.
Well, Bash is Born Again Shell. It's is a very popular command line interpreter or shell. It has a very long history starting with first Unix shell called Thompson Shell in 1971. Thompson shell has very minimalised features and lack many important features and is replaced with Mashey Shell in 1975. That inturn was replaced by Bourne Shell (sh) in 1977 which have many features that we still use such as command substitution, various types of loop constructs and commands such as cd, pwd and export. And also it has break/continue and getopts and someother which we will discuss later in this section. Some of the features are got from other project ALGOL. However it was criticized for not being friendly to use and C shell (csh) is introduced in 1978 and had a style more similar to C language and Bourne shell is more similar to ALGOL.
In 1989 BASH was released as a free alternative to BOURNE shell under the new project which promotes the freedom of usage to run, share, study and modify software. BrainFox is the original author of Bash. Bash is the default shell on Mac OS and Linux making it incredibly widespread. As of now the current version of Bash is 4.4.
You can find more information about Bash at https://tiswww.case.edu/php/chet/bash/bashtop.html.
In recent times i faced a situation where i need to write some bash scripts in my project that finally lead me to update my blog with Bash course. This course will guide you through various bash commands and also about writing bash scripts. This course addresses all the concepts with real time examples.
So let's get started.
What is Bash?
Well, Bash is Born Again Shell. It's is a very popular command line interpreter or shell. It has a very long history starting with first Unix shell called Thompson Shell in 1971. Thompson shell has very minimalised features and lack many important features and is replaced with Mashey Shell in 1975. That inturn was replaced by Bourne Shell (sh) in 1977 which have many features that we still use such as command substitution, various types of loop constructs and commands such as cd, pwd and export. And also it has break/continue and getopts and someother which we will discuss later in this section. Some of the features are got from other project ALGOL. However it was criticized for not being friendly to use and C shell (csh) is introduced in 1978 and had a style more similar to C language and Bourne shell is more similar to ALGOL.
In 1989 BASH was released as a free alternative to BOURNE shell under the new project which promotes the freedom of usage to run, share, study and modify software. BrainFox is the original author of Bash. Bash is the default shell on Mac OS and Linux making it incredibly widespread. As of now the current version of Bash is 4.4.
You can find more information about Bash at https://tiswww.case.edu/php/chet/bash/bashtop.html.
Reviewing common Bash commands
pwd - Prints the current working directory. That means it tells you where you currently are.
ls - Lists files and folders in current working directory
ls -l [Folder/File name] - If folder name is given, it lists all the files inside the folder along with Permissions, File creation/updation date and so on. If the file name is given, the same file will be shown with Permissions, File creation/updation date and so on.
In the above picture, the first column shows the permissions of the directory or the file. Also the starting character d in the first column indicates that it is the directory. Otherwise it simply shows -
man ls - It shows all the options available to use with ls command. man command provides the comprehensive guide for all the commands.
mkdir [folder] - Creates a directory with the specified folder name
rmdir[folder] - Deletes the directory
clear - Clears the screen
cd [dir_name] - Change to the specified directory
cp [src_file_name] [dest_file_name] - Creates a copy of src_file_name and is named with dest_file_name
rm [file_name] - remove the specified file
cat [file_name] - See the contents of the file
more [file_name] - You can see the long file in the form of pagination. That means you can see the file page by page. Press Space bar to move through the pages and then press q when you are done.
head [file_name] - Peek at the beginning of the file.
tail [file_name] - View the end of the file. This is very handy for the log files
cd ~ Points to the user home variable
cd~- is the PWD
echo ~- shows the PWD
The other handy expansion is called brace expansion.
Ex: touch{apple, banana, cherry, pineapple}
It creates the files with the specified names. You no need to run touch command multiple times. For suppose if i want to create 1000 files then it would be tedious to run touch command those many times. So i can do it in the easy way. See below
touch file_{1..1000}
Try the command and check whether the files are created with command ls
echo{1..10} - Prints the numbers from 1 to 10
rm * - Removes all the files in the existing directory
echo{1..10..2} - output will be 1 3 5 7 9
echo{1..10..3} - output will be 1 4 7 10
echo{A..Z} - Output from A - Z
echo{A..z} - Output from A - Z following with a - z
touch {apple, banana, cherry}_{01..100}_{w..d} - Create files with those three combinations. Just try the command and see.
ls | more - List of files will be displayed in the form of pagination. Press Spacebar to move page by page.
Example: I create a new folder called otherfolder and change the permissions of some of the files (files which match the pattern _015) in the current folder. Later i copy the files to otherfolder from current folder using cp. -v option along with cp command means to turn on verbose. It means that it displays copy command output for each of the file it copies to the otherfolder. I am specifying * to specify file name expansions to match all of the files.
mkdir ../otherfolder
chmod 000 *_015*
cp -v * ../otherfolder
If you execute the above command, you will see that copy operation for the files which match the pattern _015 are not copied because they have restricted file permissions. I did that using chmod in the above example.
cp -v * ../otherfolder 1>../success.txt 2> ../error.txt
The number 1 and 2 represent standard output and standard error respectivly. > symbol represents redirecting the output to the success.txt and error.txt. So all the files which were copied successfully to the otherfolder will be written to success.txt and the files which were not copied to otherfolder will be written to the file error.txt.
cat ../success.txt - Shows the contents of the file
cp -v ../otherfolder &> ../log.txt - This command instead of using 1 and 2 as shown in the above command just use &> to store all the output of command to the file log.txt.
grep --color=auto malathi auth.log - auth.log is the log file which have my name malathi. when i execute this command, my name malathi is highlighted with a color.
export GREP_OPTIONS='--color=auto' - This is another way to enable color=auto option of grep command. But the option is enabled at all the time. When grep command is executed, the content of the auth.log file will be displayed and the search word will be highligted with a color
grep -i break-in auth.log -
grep -i break-in auth.log | awk{'print $12'} -
man awk
man sed
In the above picture, the first column shows the permissions of the directory or the file. Also the starting character d in the first column indicates that it is the directory. Otherwise it simply shows -
man ls - It shows all the options available to use with ls command. man command provides the comprehensive guide for all the commands.
mkdir [folder] - Creates a directory with the specified folder name
rmdir[folder] - Deletes the directory
clear - Clears the screen
cd [dir_name] - Change to the specified directory
cp [src_file_name] [dest_file_name] - Creates a copy of src_file_name and is named with dest_file_name
rm [file_name] - remove the specified file
cat [file_name] - See the contents of the file
more [file_name] - You can see the long file in the form of pagination. That means you can see the file page by page. Press Space bar to move through the pages and then press q when you are done.
head [file_name] - Peek at the beginning of the file.
tail [file_name] - View the end of the file. This is very handy for the log files
cd ~ Points to the user home variable
cd~- is the PWD
echo ~- shows the PWD
The other handy expansion is called brace expansion.
Ex: touch{apple, banana, cherry, pineapple}
It creates the files with the specified names. You no need to run touch command multiple times. For suppose if i want to create 1000 files then it would be tedious to run touch command those many times. So i can do it in the easy way. See below
touch file_{1..1000}
Try the command and check whether the files are created with command ls
echo{1..10} - Prints the numbers from 1 to 10
rm * - Removes all the files in the existing directory
echo{1..10..2} - output will be 1 3 5 7 9
echo{1..10..3} - output will be 1 4 7 10
echo{A..Z} - Output from A - Z
echo{A..z} - Output from A - Z following with a - z
touch {apple, banana, cherry}_{01..100}_{w..d} - Create files with those three combinations. Just try the command and see.
Pipes
It is one of the powerful feature that is used to run multiple commands at a time
Example: I create a new folder called otherfolder and change the permissions of some of the files (files which match the pattern _015) in the current folder. Later i copy the files to otherfolder from current folder using cp. -v option along with cp command means to turn on verbose. It means that it displays copy command output for each of the file it copies to the otherfolder. I am specifying * to specify file name expansions to match all of the files.
mkdir ../otherfolder
chmod 000 *_015*
cp -v * ../otherfolder
If you execute the above command, you will see that copy operation for the files which match the pattern _015 are not copied because they have restricted file permissions. I did that using chmod in the above example.
cp -v * ../otherfolder 1>../success.txt 2> ../error.txt
The number 1 and 2 represent standard output and standard error respectivly. > symbol represents redirecting the output to the success.txt and error.txt. So all the files which were copied successfully to the otherfolder will be written to success.txt and the files which were not copied to otherfolder will be written to the file error.txt.
cat ../success.txt - Shows the contents of the file
cp -v ../otherfolder &> ../log.txt - This command instead of using 1 and 2 as shown in the above command just use &> to store all the output of command to the file log.txt.
Grep
This is a handy tool which allows to search files for dpecific pattern of text. This comes in handy when you are searching through the log files.grep --color=auto malathi auth.log - auth.log is the log file which have my name malathi. when i execute this command, my name malathi is highlighted with a color.
export GREP_OPTIONS='--color=auto' - This is another way to enable color=auto option of grep command. But the option is enabled at all the time. When grep command is executed, the content of the auth.log file will be displayed and the search word will be highligted with a color
grep -i break-in auth.log -
grep -i break-in auth.log | awk{'print $12'} -
man awk
man sed
Understanding Bash script syntax
Bash script syntax
#! /bin/bash
# comment
commands
more commands
other commands
The script will start with interpretor directive that sometimes called hashbang or shebang. The nick name comes from first two characters in the line #!. bin/bash is the path of the bash executable and this vary on different systems but usually it will be a bin/bash. The whole line tells the shell that this is a bash script and should be executed as such. For example if you are trying to execute bash script in cygwin, this piece of line indicates that it should be run as bash script by the shell.
# sign is used to comment out the lines. The interpretor will just ignore such lines.
Example: I am using cygwin inorder to write bash script and execute them. Please download cygwin and install it in your machines. After installing, open the cygwin shell and create a bash script.
> vi my.sh
#! bin/bash
# This is a sample bash script
ls
Save the file by pressing :wq. Now you will be returned back to the terminal. Now execute the bash script using the command
bash my.sh
This list all the files in the current working directory. You can also execute the bash script using ./my.sh but you will get an error saying permission denied. So you need to change the permissions of the script using the command
chmod +x my.sh
Now if you run the bash script again using ./my.sh then it executes successfully without any error.
We need to put ./ because the current working directory is not in PATH environment variable. If we copy the file which is the in PATH environment variable, then you just need to type my.sh
> cp my.sh /usr/bin
> my.sh
But we will not do it practically. Just for the knowledge i am making note of it here.
echo $e
((e-=5))
echo $e
Let's take an another example usecase
f=$((1/3))
It actually prints 0 in the output because bash Math works only with integers not floating point number. To work with floating point values, you need to use bc program within the script.
Example
f=$(echo 1/3 | bc -l)
echo $f
If you run now, you will see the output as 0.3333333333. Just run it and see.
If you want to learn more about bc command, type man bc command and check.
[[ expression ]]
1: FALSE
0: TRUE
If the expression returns 1 then it is false and if it returns 0 then it is true.
Let's take an example for Comparing Strings
Example
#! /bin/bash
# This demonstrates bash String comparison operators
[[ "cat" == "cat" ]]
echo $?
[[ "cat" == "dog" ]]
echo $?
Below is the list of operators available for comparing Strings
Example for comparing integers
[[ 20 -gt 10]]
echo $?
[[ 30 -lt 10]]
echo $?
Below is the list of operators available for comparing Integers.
There are also operators to test whether the String is NULL or NOT NULL. option -z is used to test whether the String is null or empty an option -n test whether the String is not null.
let's take an example
a=""
b="cat"
[[ -z $a && -n $b]]
echo $?
If you are finding examples online or working with older scripts you might see a notation with single square brackets. Double square brackets are introduced in bash version 2.2 onwards.
a="hello"
b="world"
c=$a$b
echo $c
output: helloworld
To find the length of the String, use the following command.
echo ${#a}
output: 5
To extract substring of the String, use the following command. Note that the index will start from 0. The below command will output the substring of helloworld starting from index 3 to the end of the string.
d=${c:3}
echo $d
output: loworld
We can also specify start index and endindex to extract the substring of the given string. See below command. Here 3 is startIndex and 4 is endIndex.
e=${c:3:4}
output: lowo
If we specify the negative number then it starts from the end of the String towards the starting of the String. The below command will extract the substring of String by traversing from end of the String until the index 4.
echo ${c: -4}
output: orld
The following command extract the first three letters of the last four letters in the String.
echo ${c: -4:3}
output: rld
The following command replaces the one String with the other. In the below example, it searches for the first occurence of the String banana and replaces that with pineapple. It does not replace the second occurence.
fruit="apple banana banana cherry"
echo ${fruit/banana/pineapple}
output: apple pineapple banana cherry
Now if you want to replace all the occurences of the String, use the following command. We need to just add two slashes (//) instead of one.
fruit="apple banana banana cherry"
echo ${fruit//banana/pineapple}
output: apple pineapple pineapple cherry
We also have couple of modifiers that we need to know. The below command have a sign # infront of apple. It actually search for apple in the very beginning of the String.
fruit="apple banana banana cherry"
If we want to replace the very end of the String, the following command works. It actually search for the cherry at the very end of the String.
fruit="durian banana banana cherry"
Styling options are below
Example:
flashred=$(tput setab 0; tput setaf 1; tput blink)
red=$(tput setab 0; tput setaf 1)
none=$(tput sgr0)
echo -e $flashred"ERROR: $none$red"Something went wrong."$none
Thu Oct 17 21:06:18 UTC 2018
But we can use date formatting options like below. Let's take some examples.
date + "%d-%m-%Y"
output: 17-10-2018
Inorder to extract only the time in the desired format, use the below command
date +"%H-%M-%S"
output: 21:07:31
you can always use man command to explore more about particualr command. So now i discuss little bit about printf command. While you can use echo to print the text but printf gives lot more options.
printf "Name:\t%s\nID:\t%04d\n" "Malathi" "12"
output:
Name: Malathi
ID: 0012
In the above command \t is the TAB and \n is the newline character, %4d will pad the field ID with 4 zero's if we dont specify a number. If we specify a number of length less than 4 then it will prefix the number with those many number of zero's which as a whole make the number with the length 4.
printf "Name:\t%s\nID:\t%04d\n" "Malathi"
Name: Malathi
ID: 0000
Now let's write a bash script using date and printf commands
#! /bin/bash
# This demonstrates date and printf commands
today=$(date +"%d-%m-%Y")
time=$(date +"%H:%M:%S")
printf -v d "Current User: \t%s\nDate:\t\t%s @ %s\n" $USER $today $time
echo $d
output:
Current User: Malathi
Date : 02-06-2018 @ 21:16:55
There are lot of format specifiers to use. I encourage you to check them out at the following url
http://wiki.bash-hackers.org/commands/builtin/printf
! /bin/bash
# This demonstrates Arrays
a=()
b=("apple" "banana" "cherry")
echo ${b[2]}
output: cherry
We can also set array values by index numbers. This is very useful because instead of adding elements to the end of the array by default, we can add an element at particular index.
! /bin/bash
# This demonstrates Arrays
a=()
b=("apple" "banana" "cherry")
echo ${b[2]}
b[5]="kiwi" # Add kiwi at index 5 in the array
b+=("Mango") # Add Mango to the end of the array
echo ${b[@]} # Prints the whole array
echo ${b[@]: -1} # It prints the last element in the array
output
cherry
apple banana cherry kiwi Mango
Mango
We can also create associate arrays. That is we can specify key and a value. See the below example
! /bin/bash
# This demonstrates Associated Arrays
declare -A myarray
myarray[color]=blue
myarray["Shirt"]="T-Shirt"
echo ${myarray["Shirt"]} is ${myarray["color"]}
output
T-Shirt is blue
The most interesting thing is we can write a set of instructions in the text file, read and execute them automatically. Let's take some good example. Name the file something like ftp.txt
open mirrors.xmission.com // connect to site
user anonymous nothinghere // user as anonymous and password as nothinghere
ascii // type of connection
cd gutenberg // Change the directory to gutenberg
get GUTINDEX.00 // fetches the file
>ftp -n < ftp.txt
Here -n option allows to skip the login user being the current session user of shell and connect with whatever credentials the file contains.
if expression
then
echo "True"
elif expression2; then
echo "Expression 2 is True"
fi
Try the below examples for better understanding.
Example 1
#!/bin/bash
# This demonstrates if loop
if [ $a -gt 4 ]; then
echo $a is greater than 4!
else
echo $a is not greater than 4!
fi
Example 2
In this example we use regular expression to check whether the String contains numbers. To do that we use =~ to indicate that it is uses regular expression.
#!/bin/bash
# This demonstrates if loop and basic regular expression
if [ [ $a =~ [0-9]+ ] ]; then
echo "There are numbers in the String"
else
echo "There are no numbers in the String"
fi
#!/bin/bash
# This demonstrates while loop
i=0
while [ $i -le 10]; do
echo i:$i
((i+=1))
done
j=0
until [ $j -ge 10 ]; do
echo j:$j
((j+=1))
done
#!/bin/bash
# This demonstrates if loop
for i in 1 2 3
do
echo $i
done
Example 2
#!/bin/bash
# This demonstrates if loop
for i in {1..100}
do
echo $i
done
#!/bin/bash
# This demonstrates if loop
for i in {1..100..2}
do
echo $i
done
Example 4
#!/bin/bash
# This demonstrates if loop
for (( i = 0; i <= 10; i++))
do
echo $i
done
Example 5
#!/bin/bash
# This demonstrates if loop
arr=("apple" "banana" "cherry")
for i in ${arr[@]}
do
echo $i
done
Now we work with command substitution in for loop. In our example we use ls command. For loop through the output of ls command and print out the result.
#!/bin/bash
# This demonstrates if loop
for i in $(ls)
do
echo "$i: ${arr[$i]}"
done
To test number of different things we could use lot of if statements but there is better way to do it. That is case statement. Let's take an example
#!/bin/bash
# This demonstrates if loop
a="dog"
case $a in
cat) echo "Cat";;
dog|puppy) echo "dog or puppy";;
*) echo "No match!";;
esac
In above example $@ takes any number of arguments and while we are calling function numberthings we are passing command substitution. This actually passes list of the files in the current directory and finally inside the function we loop through them and echo each one of them.
Similarly we can also pass some array of values to function.
#! /bin/bash
# comment
commands
more commands
other commands
The script will start with interpretor directive that sometimes called hashbang or shebang. The nick name comes from first two characters in the line #!. bin/bash is the path of the bash executable and this vary on different systems but usually it will be a bin/bash. The whole line tells the shell that this is a bash script and should be executed as such. For example if you are trying to execute bash script in cygwin, this piece of line indicates that it should be run as bash script by the shell.
# sign is used to comment out the lines. The interpretor will just ignore such lines.
Example: I am using cygwin inorder to write bash script and execute them. Please download cygwin and install it in your machines. After installing, open the cygwin shell and create a bash script.
> vi my.sh
#! bin/bash
# This is a sample bash script
ls
Save the file by pressing :wq. Now you will be returned back to the terminal. Now execute the bash script using the command
bash my.sh
This list all the files in the current working directory. You can also execute the bash script using ./my.sh but you will get an error saying permission denied. So you need to change the permissions of the script using the command
chmod +x my.sh
Now if you run the bash script again using ./my.sh then it executes successfully without any error.
We need to put ./ because the current working directory is not in PATH environment variable. If we copy the file which is the in PATH environment variable, then you just need to type my.sh
> cp my.sh /usr/bin
> my.sh
But we will not do it practically. Just for the knowledge i am making note of it here.
Working with variables
We will start with a bash script example and go through it.
#! /bin/bash
# This script demonstrates about variables
a=Hello
b="Good Morning"
c=17
echo $a
echo $b
echo $c
echo "$b! I have $c apples."
As you notice you won't see spaces around = sign when you declare variables a,b and c. If you add spaces around them, error will be thrown when you run the script. So please make note that there are no spaces around = sign when you declare variables. If there are spaces in the Strïng then you need to enclose the Strïng within double quotes. If you notice a and b variables in the above script, variable a is without double quotes and b is with double quotes because String in b is with spaces. So we require double quotes to avoid error.
So finally to use these variables, we call them using $ sign infront of them. As you all know echo is used to print hte result to standard output.
Adding attributes to variables
declare -i d=123 # Marks the variable as an integer
declare -r e=456 # Marks the variable as read only. It means you can not modify the value.
declare -l f="Cats" # USed to convert strings to lower case
declare -u g="Dogs" #USed to convert strings to upper case
Built-in variables
echo $HOME - returns the users home directory
echo $PWD - Returns the user current working directory.
echo $MACHTYPE - Returns the machine type which could come handy to determine file location if you are writing scripts to work on different platforms
echo @HOSTNAME - Returns the system name.
echo $BASH_VERSION - Returns the version of bash running.
echo $SECONDS - Retuns the number of seconds the BASH session has been running.
If you type echo $SECONDS at the command line, you will know how much time the session has been open. But if you use it in the script, it will start counting from when the script started. This is handy for timing things.
echo $0 - Returns the name of the script. If you use it inside of bash script, it prints the name of the script.
If you want to explore more, you can visit the following website
Working with numbers
Working with number in bash is pretty stright forward. To tell the interpretor to deal with Math, you need to enclose the expression inside (()). Example:: (( expression ))
Example
d=2
e=$((d+2))
echo $e
((e++))
echo $e
((e--))
echo $e
((e+=5))
echo $e
((e*=3))
echo $e
((e/=3))echo $e
((e-=5))
echo $e
Let's take an another example usecase
f=$((1/3))
It actually prints 0 in the output because bash Math works only with integers not floating point number. To work with floating point values, you need to use bc program within the script.
Example
f=$(echo 1/3 | bc -l)
echo $f
If you run now, you will see the output as 0.3333333333. Just run it and see.
If you want to learn more about bc command, type man bc command and check.
Comparing values
Bash uses the following syntax to compare values. It is important to keep spaces aroung the expression.[[ expression ]]
1: FALSE
0: TRUE
If the expression returns 1 then it is false and if it returns 0 then it is true.
Let's take an example for Comparing Strings
Example
#! /bin/bash
# This demonstrates bash String comparison operators
[[ "cat" == "cat" ]]
echo $?
[[ "cat" == "dog" ]]
echo $?
Below is the list of operators available for comparing Strings
Example for comparing integers
[[ 20 -gt 10]]
echo $?
[[ 30 -lt 10]]
echo $?
Below is the list of operators available for comparing Integers.
There are also Logical operators available in bash. See the picture below
let's take an example
a=""
b="cat"
[[ -z $a && -n $b]]
echo $?
If you are finding examples online or working with older scripts you might see a notation with single square brackets. Double square brackets are introduced in bash version 2.2 onwards.
Working with strings
Concatenate Stringsa="hello"
b="world"
c=$a$b
echo $c
output: helloworld
To find the length of the String, use the following command.
echo ${#a}
output: 5
To extract substring of the String, use the following command. Note that the index will start from 0. The below command will output the substring of helloworld starting from index 3 to the end of the string.
d=${c:3}
echo $d
output: loworld
We can also specify start index and endindex to extract the substring of the given string. See below command. Here 3 is startIndex and 4 is endIndex.
e=${c:3:4}
output: lowo
If we specify the negative number then it starts from the end of the String towards the starting of the String. The below command will extract the substring of String by traversing from end of the String until the index 4.
echo ${c: -4}
output: orld
The following command extract the first three letters of the last four letters in the String.
echo ${c: -4:3}
output: rld
The following command replaces the one String with the other. In the below example, it searches for the first occurence of the String banana and replaces that with pineapple. It does not replace the second occurence.
fruit="apple banana banana cherry"
echo ${fruit/banana/pineapple}
output: apple pineapple banana cherry
Now if you want to replace all the occurences of the String, use the following command. We need to just add two slashes (//) instead of one.
fruit="apple banana banana cherry"
echo ${fruit//banana/pineapple}
output: apple pineapple pineapple cherry
We also have couple of modifiers that we need to know. The below command have a sign # infront of apple. It actually search for apple in the very beginning of the String.
fruit="apple banana banana cherry"
echo ${fruit/#apple/durian}
output: durian banana banana cherryIf we want to replace the very end of the String, the following command works. It actually search for the cherry at the very end of the String.
fruit="durian banana banana cherry"
echo ${fruit/%cherry/durian}
output: durian banana banana durianColoring and Styling text
Let's see how to color and style the output of the command using tput. But before that we need to know the the color codes and styling options. Color codes will raneg from 0 - 7 both for foreground and background.
Styling options are below
Example:
flashred=$(tput setab 0; tput setaf 1; tput blink)
red=$(tput setab 0; tput setaf 1)
none=$(tput sgr0)
echo -e $flashred"ERROR: $none$red"Something went wrong."$none
Exploring some handy helpers: date and printf
date is not part of bash but it is really useful for certain kind of scripts. The date command gives you the format like belowThu Oct 17 21:06:18 UTC 2018
But we can use date formatting options like below. Let's take some examples.
date + "%d-%m-%Y"
output: 17-10-2018
Inorder to extract only the time in the desired format, use the below command
date +"%H-%M-%S"
output: 21:07:31
you can always use man command to explore more about particualr command. So now i discuss little bit about printf command. While you can use echo to print the text but printf gives lot more options.
printf "Name:\t%s\nID:\t%04d\n" "Malathi" "12"
output:
Name: Malathi
ID: 0012
In the above command \t is the TAB and \n is the newline character, %4d will pad the field ID with 4 zero's if we dont specify a number. If we specify a number of length less than 4 then it will prefix the number with those many number of zero's which as a whole make the number with the length 4.
printf "Name:\t%s\nID:\t%04d\n" "Malathi"
Name: Malathi
ID: 0000
Now let's write a bash script using date and printf commands
#! /bin/bash
# This demonstrates date and printf commands
today=$(date +"%d-%m-%Y")
time=$(date +"%H:%M:%S")
printf -v d "Current User: \t%s\nDate:\t\t%s @ %s\n" $USER $today $time
echo $d
output:
Current User: Malathi
Date : 02-06-2018 @ 21:16:55
There are lot of format specifiers to use. I encourage you to check them out at the following url
http://wiki.bash-hackers.org/commands/builtin/printf
Working with Arrays
Inorder to keep track of collection of things we use arrays. Let's take a look at some examples.! /bin/bash
# This demonstrates Arrays
a=()
b=("apple" "banana" "cherry")
echo ${b[2]}
output: cherry
We can also set array values by index numbers. This is very useful because instead of adding elements to the end of the array by default, we can add an element at particular index.
! /bin/bash
# This demonstrates Arrays
a=()
b=("apple" "banana" "cherry")
echo ${b[2]}
b[5]="kiwi" # Add kiwi at index 5 in the array
b+=("Mango") # Add Mango to the end of the array
echo ${b[@]} # Prints the whole array
echo ${b[@]: -1} # It prints the last element in the array
output
cherry
apple banana cherry kiwi Mango
Mango
We can also create associate arrays. That is we can specify key and a value. See the below example
! /bin/bash
# This demonstrates Associated Arrays
declare -A myarray
myarray[color]=blue
myarray["Shirt"]="T-Shirt"
echo ${myarray["Shirt"]} is ${myarray["color"]}
output
T-Shirt is blue
Reading and Writing Text files
Working with text files in bash is easy. Let's see some examples now
# it stores the text to file.txt. file.txt will be created automatically if it doesn't exists.
echo "Some text" > file.txt
# The above command overrides the contents of the file but inorder to append the content use >>
echo "Some more text" >> file.txt
What about reading files
#! /bin/bash
# This demonstrates reading content from file line by line
i=1
while read f; do
echo "Line $i: $f"
((i++))
done < file.txtThe most interesting thing is we can write a set of instructions in the text file, read and execute them automatically. Let's take some good example. Name the file something like ftp.txt
open mirrors.xmission.com // connect to site
user anonymous nothinghere // user as anonymous and password as nothinghere
ascii // type of connection
cd gutenberg // Change the directory to gutenberg
get GUTINDEX.00 // fetches the file
>ftp -n < ftp.txt
Here -n option allows to skip the login user being the current session user of shell and connect with whatever credentials the file contains.
Control Structures
First let's take a syntax and example about if loopif expression
then
echo "True"
elif expression2; then
echo "Expression 2 is True"
fi
Try the below examples for better understanding.
Example 1
#!/bin/bash
# This demonstrates if loop
if [ $a -gt 4 ]; then
echo $a is greater than 4!
else
echo $a is not greater than 4!
fi
Example 2
In this example we use regular expression to check whether the String contains numbers. To do that we use =~ to indicate that it is uses regular expression.
#!/bin/bash
# This demonstrates if loop and basic regular expression
if [ [ $a =~ [0-9]+ ] ]; then
echo "There are numbers in the String"
else
echo "There are no numbers in the String"
fi
Working with While and Until loops
Let's work with while and until loops now. Please try the below examples and see how the output looks.#!/bin/bash
# This demonstrates while loop
i=0
while [ $i -le 10]; do
echo i:$i
((i+=1))
done
j=0
until [ $j -ge 10 ]; do
echo j:$j
((j+=1))
done
Working with For loops
Example 1#!/bin/bash
# This demonstrates if loop
for i in 1 2 3
do
echo $i
done
Example 2
#!/bin/bash
# This demonstrates if loop
for i in {1..100}
do
echo $i
done
#!/bin/bash
# This demonstrates if loop
for i in {1..100..2}
do
echo $i
done
Example 4
#!/bin/bash
# This demonstrates if loop
for (( i = 0; i <= 10; i++))
do
echo $i
done
Example 5
#!/bin/bash
# This demonstrates if loop
arr=("apple" "banana" "cherry")
for i in ${arr[@]}
do
echo $i
done
Now we take a look at Associative arrays. Please note Associative arrays are available from Bash 4
#!/bin/bash
# This demonstrates if loop
declare -A arr
arr["name"]="Malathi"
arr["id"]="12"
for i in "${!arr[@]}"
do
echo "$i: ${arr[$i]}"
done
# This demonstrates if loop
declare -A arr
arr["name"]="Malathi"
arr["id"]="12"
for i in "${!arr[@]}"
do
echo "$i: ${arr[$i]}"
done
Now we work with command substitution in for loop. In our example we use ls command. For loop through the output of ls command and print out the result.
#!/bin/bash
# This demonstrates if loop
for i in $(ls)
do
echo "$i: ${arr[$i]}"
done
Selecting behaviour using CASE
To test number of different things we could use lot of if statements but there is better way to do it. That is case statement. Let's take an example
#!/bin/bash
# This demonstrates if loop
a="dog"
case $a in
cat) echo "Cat";;
dog|puppy) echo "dog or puppy";;
*) echo "No match!";;
esac
Using functions
As you all know how important the functions are in every language and now we see how functions are built inside bash script. Let's take a look at different examples of functions and how we pass arguments to function and access them.
Example 1
#!/bin/bash
# This demonstrates functions
function greet{
echo "Hi there!"
}
echo "Greeting - "
greet
Example 2
We can also pass arguments to the functions. See below.
#!/bin/bash
# This demonstrates functions
function greet{
echo "Hi there! $1 $2"
}
echo "Greeting - "
greet Malathi "How are you"
Example 3
#!/bin/bash
# This demonstrates functions
function numberthings{
i=1
for f in $@; do
echo $i: $f
((i+=1))
done
}
numberthings $(ls)
numberthings apple banana cherry
# This demonstrates functions
function greet{
echo "Hi there!"
}
echo "Greeting - "
greet
Example 2
We can also pass arguments to the functions. See below.
#!/bin/bash
# This demonstrates functions
function greet{
echo "Hi there! $1 $2"
}
echo "Greeting - "
greet Malathi "How are you"
Example 3
#!/bin/bash
# This demonstrates functions
function numberthings{
i=1
for f in $@; do
echo $i: $f
((i+=1))
done
}
numberthings $(ls)
numberthings apple banana cherry
In above example $@ takes any number of arguments and while we are calling function numberthings we are passing command substitution. This actually passes list of the files in the current directory and finally inside the function we loop through them and echo each one of them.
Working with arguments
If the user want to pass some input to the bash script from the shell, we can do it as below
Example 1: let's name the script as test.sh
#!/bin/bash
# This demonstrates passing arguments to the script from the shell.
echo $1
echo $2
Now while running the script, we pass input to the script or we can call them arguments too. In below example Malathi will be the first arguement and Boggavarapu will be the second argument to the script.
>test.sh Malathi Boggavarapu
output :
Malathi
Boggavarapu
Example 2: In this exmple we will see how to pass arbitrary number of arguments to the script and how to make the script ready to accept multiple number of arguments
#!/bin/bash
# This demonstrates passing arguments to the script from the shell.
for i in @$
do
echo $i
done
echo "There are $# arguments"
>test.sh banana apple cherry kiwi orange
output:
banana
apple
cherry
kiwi
orange
There are 5 arguments
Working with flags
If you have been working with bash command line environment for a while you are bound to know about flags. There are specific options where you can pass information to a program and they ususally start with a -. You can make use of them in bash script by getopts.
Example: let's name the file as my.sh
#!/bin/bash
# This demonstrates the usage of flags
while getopts u:p: option; do
case $option in
u) user=$OPTARG;;
p) pass=$OPTARG;;
esac
done
echo "User; $user / Pass: $pass"
we run the above script using the following command
./my.sh -u malathi -p secret
output:
User: malathi / Pass: secret
Example 2: If we specify some options without colon (:) between them, it means that the bash script verify whether the flags that are passed in are being used inside the bash script. See below program.
#!/bin/bash
# This demonstrates the usage of flags
while getopts u:p:ab option; do
case $option in
u) user=$OPTARG;;
p) pass=$OPTARG;;
a) echo "Got the a flag";;
b) echo "Got the b flag";;
esac
done
echo "User; $user / Pass: $pass"
we run the above script using the following command
./my.sh -u malathi -p secret -a
output:
User: malathi / Pass: secret
Got the a flag
./my.sh -u malathi -p secret -a -b
output:
User: malathi / Pass: secret
Got the a flag
Got the b flag
Example 3: If you specify : before u, it means that if we pass some unknown flag which is not defined inside the script then it goes to the case statement ?)
#!/bin/bash
# This demonstrates the usage of flags
while getopts :u:p:ab option; do
case $option in
u) user=$OPTARG;;
p) pass=$OPTARG;;
a) echo "Got the a flag";;
b) echo "Got the b flag";;
?) echo "I dont know what $OPTARG is";;
esac
done
echo "User; $user / Pass: $pass"
./my.sh -u malathi -p secret -a -b -z
output:
User: malathi / Pass: secret
Got the a flag
Got the b flag
I dont know what z is
Getting input during execution
You just saw how to get user input at command line. Sometimes it is not practical to have all of the values you are looking for specified when you run the script. There is a way to get input during the execution of the script. That is using read. Let's take an example
#!/bin/bash
# This demonstrates getting input during execution
echo "What is your name?"
read name
echo "What is your password"
read -s pass # -s is silent. Whatever you write is not visible
read -p "what is your favourite sport?" sport # -p is used to write everything on one line
echo name: $name, Password: $pass, sport: $sport
Just try the example. You will understand how it works
There are some options as well and i encourage you to explore more at following url
Example 2
#!/bin/bash
# This demonstrates getting input during execution
select fruit in "apple" "banana" "cherry"
do
echo "You selected $fruit"
break
done
Run at command line using
./my.sh
1)apple
2)banana
3)cherry
#? 3
output: You selected cherry
Example 3
#!/bin/bash
# This demonstrates getting input during execution
select fruit in "apple" "banana" "cherry"
do
case $fruit in
apple) echo "Apple is sweet";;
banana) echo "Banana is also sweet";;
cherry) echo "Cherry is sour";;
*) echo "I don't know what the option is";;
esac
done
Test the script as said in above example.
Ensuring the response
We should also build some error tolerance in the scripts. What happens if the user does not enter any input and simply press enter?
Example
#!/bin/bash
if [ $# -lt 3 ]; then
cat <<- EOM
This command requires 3 arguments;
username, userid and favorite number
EOM
else
echo "Username: $1"
echo "Userid; $2"
echo "Favourite number: $3"
fi
If you don't provide any arguments while running the script, it shows the message for the user. Otherwise it prints the result.
Example 2: Please test it
#!/bin/bash
read -p "Favourite sport? " a
while [[ -z "$a" ]]; do
read -p "I need an answer" a
done
echo "$a is selected"
Example 3: The below script accepts the year with only the desired format specified. We use regular expression to validate the input of the user. Just try the example and you will understand it better.
#!/bin/bash
read -p "What year? [nnnn] " a
while [[ ! $a =~ [0-9]{4} ]]; do
read -p "A year, please [nnnn]" a
done
echo "Selected year $a"
We came to the end of the session. Hope it is helpful. Please post your comments below.
Happy Learning!!
Comments
Post a Comment