If you’ve spent any amount of time running a Linux server then you’ll know what a time sink it can be. Especially in a development environment, where debugging stuff will often have you running the same tasks over and over again, which If you ask me, it’s the worst thing you can do to yourself as a developer. Do yourself a favor and whip out that terminal because in today’s guide we will be using bash scripts to work smarter not harder!
Getting started
I’m going to assume you are at least semi-familiar with terminal commands as I won’t be going over the basics of scripting in detail here as that is beyond the scope of this article. Instead I’ll be covering development focused scripting with in-depth explanation of what the scripts are doing, the techniques used, and so on. Stay tuned for a more comprehensive guide on scripting in the future. For now let’s go over some basic concepts to get the idea ball rolling. Then, you will be rewarded with a simple bash script example for… who knows?
Bash Script Refresher
In its simplest form a shell script is an executable sh
file with a list of commands to be executed in sequence from top to bottom. At the top of most shell scripts you’ll find the shebang #!/bin/bash
, which tells your system to use the ‘bourne-again shell’ or bash
, as the scripts’ interpreter which will parse the commands using bash and execute them.
#!/bin/bash
# Updating Debian\Ubuntu with one command.
sudo apt update && sudo apt upgrade -y
Using the shebang at the top of your script will help with portability on other systems because if omitted then you leave it up to the system to decide which interpreter to use which isn’t always bash
and it could lead to errors or other undesired operation.
Executing and running a bash script
touch
will make the file but you’ll need to give it executable permissions to get the script to run. Use chmod +x
on your script file then edit contents using your favorite text editor.
touch example.sh && chmod +x example.sh
Running a shell script can be done in a couple of ways. They are functionally the same.
./example.sh
#or
sh example.sh
Terminal STDOUT & STDERR
Every command entered in a terminal (in Linux) has 2 parts, STDOUT
& STDERR
. stdout
or standard output is what you see returned back to you in the terminal and stderror
or standard error is what gets logged away. If you call the built-in shell variable $?
using echo
you can see the exit status of the last run command. For example, on a system without tree
we should get a exit status of 1, because it’s not installed. Successful commands return a 0 on success and a 1 (or higher) on failure.
Intermediate scripting techniques.
User Prompts to pass data inside of your bash scripts
Basic scripts like the above are all well and good but the real magic happens when you include a level of interactively if the script calls for it. Let’s look at a different example:
#!/bin/bash
sudo apt install cowsay -y
# prompt and run
read -p "Enter what the cow will say:" p
cowsay -p -s ${p}
This above script uses a read -p
directive to prompt the user for some text, which is stored in the short term usage variable “p” before passing that “p” variable to cowsay
to print out a silly terminal message.
Bash Script Conditionals
Let’s take a look at a different script but this time we’ll add an if statement to determine if tree
is installed, and if it isn’t found then we’ll install it. if statements can be a bit tricky so let’s look at the bare syntax: The if statement has one condition and will execute the command if the expression evaluates to true.
if [condition]
then
command
fi
Simple enough right? In order for our script to make that check, we’ll need to write out the condition that the if statement will run on. Look at the example below, then read on for the logic breakdown.
#!/bin/bash
#check for and install tree on debian/ubuntu
if [! command -v tree > /dev/null]
then
echo "tree not found, installing..."
sudo apt install tree -y
fi
Let’s go over the logic: using command -v <package>
we can find out where the package is located (if at all). Successful output will print the file path of the given package. The 2nd part of that condition > /dev/null
is just there to send the output of that command into the /dev/null
file.
This essentially discards the output of command -v
so that it doesn’t show up in the terminal output. Using the logical operator ‘not equal to (!)’ we can get a boolean value to base our condition on. In plain English we are saying IF the result of command -v tree
is false (which as we can recall should return false on a system without tree
) then install tree
.
If-else
If you want to be a real cool kid you can add an else block into your script to really drive the point home. Like in any other programming language, the command in the else block will only run if the initial if statement is false.
#!/bin/bash
#check for and install tree on debian/ubuntu
if [! command -v tree > /dev/null]
then
echo "tree not found, installing..."
sudo apt install tree -y
else
echo "tree found, skipping.."
fi
Bash Colors
This next part is for the real scripting geeks as I feel only we would care enough to inject some color into those lines of STDOUT. ANSI colors are the one quality of life feature you’ll want to implement into your scripts. Note that this script declares colors at the tops then uses echo -e
to enable the interpretation of escape characters (eg, slash \\
) which in turn allow colors to be displayed correctly. These color variables are then called via the ${…}
variable wrapper.
#!/bin/bash
# Ansi color code variables
red="\\e[0;91m"
green="\\e[0;92m"
#resets text color to default
reset="\\e[0m"
echo 'Colored Text:'
echo -e "${red}FAILURE${reset}"
echo -e "${green}SUCCESS${reset}"
The reason why can be seen pretty clearly in the screenshot below. Using colors on a message will really make your STDOUT
really stand out, this is incredible useful when you have scripts that produce a lot of terminal readout.
Checking user permissions
what if we wanted to check if a user was sudo
before running the bash script? Using an if statement we can then create the condition that requires the script operator or Effective UID to be sudo
or root which FYI always has the UID designation of 0.
#!/bin/bash
# colors
red="\\e[0;91m"
green="\\e[0;92m"
# text reset
reset="\\e[0m"
if [ ! ${EUID} -eq 0 ]; then
echo -e "${red}Please run as root or with sudo${reset}"
exit
else
echo -e "${green}Great Success!${reset}"
fi
Putting it all together
Now that we got a few concepts under our belt we could go ahead and put all them all together and be able to interpret what this next bash script example is doing:
Installing nvm & nodejs
I’ll be installing nvm
for illustrative purposes, but this next part can be applied to any development environment, as the process is the same across 99% of development packages out there. How you get the commands you need is up to you to acquire but the magic of a basic script example like this is that anything that can be installed via the terminal can be used in a bash script.
#!/bin/bash
# colors
red="\\e[0;91m"
green="\\e[0;92m"
# text reset
reset="\\e[0m"
if ! command -v curl > /dev/null
then
echo -e "${red}curl not found, installing...${reset}"
sudo apt install curl -y > /dev/null 2>&1
else
echo -e "${green}curl found, skipping...${reset}"
fi
echo "Installing nvm for Ubuntu"
curl | bash
echo "Make nvm available for the session"
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \\. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \\. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
echo -e "${green}Installing latest Nodejs LTS${reset}"
nvm install node --lts
Pretty simple right? In the above bash script example all I’ve done is outline the commands that are usually entered in the terminal to install nodejs using nvm
. The above script uses echo statements to be included in the STDOUT
that get spat out. You can imagine how great of a boon the colors provide in larger or more complex scripts as they provide a helpful indicator of progress.
Not the prettiest but functional!
Conclusion
This is only the tip of the ice berg as shell scripting is a vast, varied, and deep topic that has many branches and techniques to explore and utilize in your workflow. Hopefully this bash script example helps some of you out there to dive into the waters of scripting! There is tons of info out there to discover and figure out, I know I’m not anywhere near the level that I want to be at in scripting so stay tuned for more bash tutorials, Thanks for reading and happy scripting!