Database administrators need to know databases as well as the operating system (OS) their database is running on. Bash scripting is where the two merge. For the beginner, bash scripting can be intimidating, but all have the same basic structure: a beginning, middle, and end.

Disclaimer: I am not a bash scripting expert. This blog will provide an elementary approach to bash scripts with some scripting and debugging tips. It is most beneficial for those with some knowledge in computer languages trying to get started interpreting and writing bash scripts.

The Beginning

This is the first line in the bash script where it lets the OS know that it is a bash script. The OS reads this line and knows the subsequent code should be run by the bash shell interpreter found in the /bin directory.

There are two ways to do this in a Unix-like OS, shown below. Each starts with a #!, often called sha-bang or hashbang. The first example is often used when the path of the bash interpreter is found in the user’s /bin directory. The second example is used when the path of the bash interpreter is unknown.

# Used when the bash interpreter is in the user's /bin directory
#!/bin/bash 

# Used when the path to the bash interpreter is unknown
#!/usr/bin/env bash

The Middle

The middle part is the bulkiest part of the bash script. This is where the executable commands go and the magic happens (when the script works) or doesn’t (when the script is not working).

Scripts are as unique as the creator. Best practice is to use comments to help explain the code so anyone can understand it. Additionally, it is a good way to create an outline of steps when building a script. The example below is how I started a script that enabled the slow query log for 15 minutes, then disabled it.

#!/bin/bash

# Set variables

# Enable slow query log

# Sleep for 15 minutes

# Disable slow query log

Starting from scratch might be too ambitious for some beginners. It is common to take components from another script to make your own. Moreover, a lot can be learned from reverse engineering someone else’s script. The table below briefly describes components of a bash script to help you understand and identify them.

ComponentsFunctionSyntax
VariablesUsed to store data (numeric or string) to make a script more versatile.2 ways to assign them:
declare — var=value
var=value

Evoking them:
$var
FunctionsValues are passed into functions, usually through variables, to be executed with the commands inside the brackets.2 ways to write them:
function function_name {
     Command
}

function_name ( ) {
     command 
}

Evoking them:
function_name
ArraysThey store data like strings and integers. Arrays can hold more than one value.  

There are 2 types of arrays:
Indexed: keys have ordered indexes starting with [0]

Associative: have key:value pairs
Creating indexed arrays:
declare -a arr
arr=(values)

Adding values to array:
arr[0]=first_value
arr[1]=second_value

Evoking values:
${arr[index]}

Creating associative arrays:
declare -A arr

Adding values to array:
arr[key]=value

Evoking values:
${arr[key]}
Boolean Operators
OrUsed to execute a command when one of two conditions need to be met to execute a command.

If the first condition isn’t met, the second condition must be true for the command to be run.
||
AndUsed to execute a command when two conditions need to be met to execute a command.

If the first condition isn’t met, the command is not executed. If the first condition is met and the second one isn’t, the command is not executed.
&&
Conditional Statements
IfUsed when a command should only be run if the specified test condition is met. 

Test condition often uses operators such as || and &&.

When brackets are used, the -gt(greater than) or  -lt (less than) are used. 

When double parentheses are used, > or < can be used instead.
2 ways to write them:
if [test_condition]; then 
   command
fi

if (( test_condition )); then 
   command
fi
if/elseIt’s an extension of the if statement. The else statement executes a command when the if statement returns false.2 ways to write them:
if [test_condition]; then 
   command
else
   command
fi

if (( test_condition )); then 
   command
else
   command
fi
caseWhen there are multiple test conditions to be met to execute different commands, a case command can be used instead of a series of if/else statements.

Test conditions usually use match operators like =, ||, or &&.
case var in
test_condition)
   command
;;
Another test_condition)
   command
;;
Another test_condition)
   command
;;
esac
Loops
whileCan be used with arrays or conditional statements. It will run commands while the test condition returns true.while test_condition
do
  command(s)
done
untilSimilar to while loops but commands will run until the test condition returns true.until test_condition
do
  command(s)
done
forStarts the value that is initiated (i=value) and will iterate up or down depending on the direction set by the executable (i++ or i--) until the test condition is met. The variable used is i and incorporated into the command that is being executed.for (( iniated_cal;  test_condition; executable ));
do
  command(s);
done

The executable code might include any of the above components. The good thing is that other computer languages have these components and the principles are basically the same with some tweaking of the syntax. Variables and arrays, for instance, are often used in computer languages to hold data. Likewise, conditional statements and loops are used in computer languages like JavaScript. If these concepts are new, there are plenty of learning modules to explore as a beginner bash scripter. I did a bash script online course from Pluralsight, Shell Scripting with Bash and Z Shell.

The End

There is no official way to end a bash script. It might just end with the last executable command. The best practice is using an exit command that exits the script when the last command is successful (returns 0) .

exit 0

Debugging

Before you start testing, you need to make sure that the script is executable. The below command can be used.

$ chmod u+x <filename>

If the bash script is not working, adding -v or -x options to the she-bang line will print out every line as the script runs. It will give you an idea of where the code is breaking. I prefer the -x because it also prints the values of all the variables as well.

# will print every line before it runs
#!/bin/bash -v 

# will print every line and the values in the script before it runs
#!/bin/bash -x 

If knowing where the code is breaking doesn’t help debugging it, there is also a command line utility, shellcheck, that automatically finds bugs in a bash script. If you do not want to install it, there is an online equivalent at https://www.shellcheck.net. Sample output below.

Bash Scripting Shellcheck Output

And that’s it.

As you can imagine, there is no one way to create a script. As a beginner, I started off really simple: no if/else statements or loops. As my skills increased, I went back to a script and added more complexity. For example, below is a script that temporarily enables the slow query log in MySQL that I created from components of someone’s script (Senior DBA, Laura Nogaj) that I knew worked. I made changes, added commands, and tested it.

#!/bin/bash 

# Setting variables for default extra file
mysqlUser='root'
mysqlPW='password'
mysqlHost='-999'
mysqlSocket='-999'
mysqlPort='-999'

# Setting variables that will name files
fileName=$HOSTNAME.bash_script_log.txt
defltsXtraFlNm=$HOSTNAME.bash.cnf

# function removes files
function func_rmFiles () {
if test -f "$1"; then
    rm -f $1
fi
}

# function creates default extra file
function func_creDfltsXtraFl () {
touch $1
chmod 755 $1
cat << EOF >> $1
[client]
user=$2
password=$3
host=$4
socket=$5
port=$6
EOF
}

# Creating default extra file
echo 'Creating defaults file' >> $fileName
    func_creDfltsXtraFl $defltsXtraFlNm $mysqlUser $mysqlPW $mysqlHost $mysqlSocket $mysqlPort

# Enabling slow query log
echo 'Enabling Slow Query Log' >> $fileName
	echo `date +%m%d%Y` >> $fileName
	echo `date +%T` >> $fileName
	mysql --defaults-extra-file=$defltsXtraFlNm -e "set global slow_query_log = 1;" 

# timer in minutes
sleep 5m

# Disable slow query log
echo 'Disabling Slow Query Log' >> $fileName
	echo `date +%m%d%Y` >> $fileName
	echo `date +%T` >> $fileName
	mysql --defaults-extra-file=$defltsXtraFlNm -e "set global slow_query_log = 0;" 

echo 'Removed Script Files' >> $fileName
func_rmFiles $defltsXtraFlNm
    
exit 0

The more you practice and challenge yourself, the more you learn and better your scripts become. Happy bash scripting!

Share This