Bash (Bourne Again Shell) is a powerful Unix shell and command language interpreter, serving as the default shell for most Linux distributions and macOS. It provides a command-line interface for interacting with the operating system, executing commands, and automating tasks through shell scripts. Bash supports variables, control structures, functions, and command substitution, making it versatile for system administration, DevOps tasks, and general-purpose scripting. Its ability to pipe commands, redirect input/output, and utilize a vast array of built-in commands and utilities makes it an essential tool for developers and system administrators in managing and automating workflows in Unix-like environments.
According to DevOps roadmap, Bash is a must have skill for everyone who wants to jump into DevOps area. So I've researched and used it. In this article, I'll list down things that I use the most and keep them as my short notes. Hope they'll help you, too.
Strings and comments
In Bash, most elements, such as variables, command names, and function names, are treated as strings by default unless used in a specific context (e.g., arithmetic or file descriptors). If a string contains spaces or special characters, it must be enclosed in single or double quotes to ensure it is treated as a single entity. Single quotes preserve the literal value of the string, while double quotes allow for variable expansion and command substitution. If a string does not contain spaces or special characters, it can be used without quotes. Here are examples.
Hello
'Hello World!'
"I'm learning Bash."
To print or display them in the terminal, use echo
command.
echo Hello
NAME='Quynh Nguyen'
echo "Hello! My name is $NAME."
echo "It's nice to meet you."
echo 'Nice to meet you, too.'
A comment starts with #
symbol, and is ignored by the interpreter.
# This is just a comment, it does nothing else.
Variables
A variable is a named storage that holds a value, typically a string, in a Bash program. Its value can be changed at any point during the execution of the program. A variable name can contain alphanumeric characters (letters and digits) and underscores (_), but it must begin with a letter or an underscore. It cannot start with a digit or contain spaces or special characters.
SUBJECT="Bash Scripting"
export CHAPTER="Variables"
Without export
keyword, a variable is local to the current shell session and is not accessible to child processes or sub shell sessions. While using export
allows it to be inherited by all child processes or sub shell sessions.
Although we can name variables with lowercase, it's a common convention in Bash to name them with uppercase, especially for environment variables or constants. This makes them stand out from regular shell commands or function names, which are typically lowercase. For example, APP_NAME and app_name are both allowed, but we should stick to APP_NAME.
APP_NAME=LARAVEL
app_name=laravel
For variables in a function or a block, I define each of them with an underscore as the prefix. It helps me figure out their scope immediately.
greet() {
local _NAME=$1
echo "Hello $_NAME"
}
if [ ! -f hello.txt ]; then
_FILE=hello.txt
touch $_FILE
fi
Using
local
makes a variable scope only inside the function.
There are a few special variables managed/assigned by the shell itself. E.g. $@
, $?
, $#
,... For more information, please visit here.
Functions
In Bash, define and call a function is simple as this following example.
# greet is the function name.
# it is followed by ()
# the function body is enclosed in {}
greet() {
if [ -z "$1" ]; then
echo "Hello, World!"
else
echo "Hello, $1!"
fi
}
Call function greet without any parameters.
# 'Hello, World!' is displayed
greet
Call function greet with a parameter.
# 'Hello, Alice!' is displayed
greet "Alice"
As you can see, when calling a function, we can pass parameters into it by placing them after the function name (separated by spaces).
Inside the function, we have $1
-$9
special variables depending on the number of parameters passed in runtime, up to 9 parameters.
concat_words() {
echo "Do you know $1 $2 $3?"
}
# $1 is "Bash"
# $2 is "is"
# #3 is "cool"
# "Do you know Bash is cool?" is printed
concat_words "Bash" "is" "cool"
Expressions
Because most elements are treated as strings, expressions are designed to interact with numbers. An expression is enclosed in $(( and ))
A=9
B=3
SUM=$((A + B))
DIFFERENCE=$((A - B))
MULTIPLICATION=$((A * B))
DIVISION=$((A / B))
echo "A + B = $SUM"
echo "A - B = $DIFFERENCE"
echo "A * B = $MULTIPLICATION"
echo "A / B = $DIVISION"
The result will be:
A + B = 12
A - B = 6
A * B = 27
A / B = 3
if / else statements
Syntax
You've already seen if / else in previous sections. It's syntax like this.
if condition; then
# Do something if the condition is true
fi
if condition; then
# Do something if the condition is true
else
# If condition is false
fi
if first_condition; then
# Do something if the first_condition is true
elif second_condition; then
# Do something if the second_condition is true
else
# Do something if neither first or second condition is true
fi
See this example for more details.
debug() {
if [ -z "$1" ]; then
echo "The debug mode is not set."
elif [ "$1" = "on" ]; then
echo "The debug mode is enabled."
elif [ "$1" = "off" ]; then
echo "The debug mode is disabled."
else
echo "The debug mode is invalid. Please use on/off."
fi
}
debug
debug invalid
debug on
debug off
Conditions
Check if a variable is not set using -z
if [ -z "$MESSAGE" ]; then
echo "MESSAGE is not set."
fi
! operator negates the condition.
if [ ! -z "$MESSAGE" ]; then
echo "MESSAGE is set."
fi
Check if a file or a directory exists.
if [ -d /var/log ]; then
echo "/var/log exists as a directory."
fi
if [ -f /var/log/alternatives.log ]; then
echo "/var/log/alternatives.log exists as a file."
fi
Check if a function exists.
if declare -F greet > /dev/null ; then
echo "greet is a function"
else
echo "greet is not a function"
fi
Check if a variable equals a string or another variable.
if [ "$BRANCH" = "main" ]; then
echo "On main branch"
fi
if [ "$BRANCH" = "$REMOTE_BRANCH" ]; then
echo "BRANCH and REMOTE_BRANCH have the same value."
fi
! operator negates the condition.
if [ "$BRANCH" != "main" ]; then
echo "Not on main branch"
fi
Number comparisons.
# -ne : not equal
if [ "$NUMBER" -ne 0 ]; then
echo "NUMBER is not 0."
fi
# -eq : equal
if [ "$NUMBER" -eq 0 ]; then
echo "NUMBER = 0"
fi
# -gt : greater than
if [ "$NUMBER" -gt 0 ]; then
echo "NUMBER > 0"
fi
# -lt : less than
if [ "$NUMBER" -lt 0 ]; then
echo "NUMBER < 0"
fi
Switch case
Switch case syntax in Bash is like this example.
case $CHOICE in
'y')
echo 'You type "y".'
;;
'n')
echo 'You type "n".'
;;
*)
echo 'Please type "y" or "n".'
;;
esac
Loops
For loop syntax in Bash is like this example.
for COLOR in red green blue
do
echo $COLOR
done
While loop syntax in Bash is like this example.
CHOICE=y
# The loop will not stop until we type "exit".
while [ "$CHOICE" != 'exit' ]
do
echo 'What is your choice?'
read -r CHOICE
done
Conclusion
These are all things about Bash I'm currently using. They help me automate my development and CI workflows easily. I think it's enough for now. If you have any other helpful cases, please share with me. I may update the post later when I found other things cool. :)
Top comments (0)