Skip to main content

Intro

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 we will be using shell scripting 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.

Scripting 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 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.

Getting the exit status of a previously run command in Linux

Intermediate scripting techniques.

User Prompts to pass data inside of your script

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.

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.

Executing a bash/shell script in Linux

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

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.

Using colors in bash/shell scripts in Linux

Obviously there are a lot more ANSI colors you can use in bash and even text styles out there, a good starter can be found on GitHub, linked here, courtesy of iamnewton.

Checking user permissions

what if we wanted to check if a user was sudo before running the 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 script 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 like this is that anything that can be installed via the terminal can be used in a 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 <https://raw.githubusercontent.com/creationix/nvm/master/install.sh> | 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 script 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.

Running an example bash/shell script with colors in Linux
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 inspires 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, Thanks for reading and happy scripting!

Leave a Reply