It's your first day at your new job. You've been handed a computer running Linux, and you were told to locate all the files containing the word "key". It's a simple enough task, right? The catch is, there are thousands of files on that system, and you've never written a shell script before.
Shell scripting, the language of the command line, is your ticket to automating repetitive tasks and mastering your Linux environment. With this guide, you'll be able to navigate your way around any Bash terminal with ease, and you might learn a couple of cool tricks along the way!
What are all these terms, anyway?
Shell
A shell is a program that interprets commands and executes them on your operating system. Simply put, a shell is a command-line interpreter that acts as a bridge between you and your operating system. It is usually run in a terminal or console. The terminal provides an input interface to run textual commands, and the shell takes in those commands and executes them on your system.
Bash
It is a shell program and command language. Bash has its roots in earlier Unix shells. The Bourne shell, released in 1977, was a major step forward in allowing users to write scripts and automate tasks. Bash, short for Bourne-Again SHell, was created in 1989 by Brian Fox as part of the GNU Project. It was designed as a free and improved alternative to the Bourne shell, while still maintaining compatibility with existing scripts.
Getting started with Bash Scripting
Setting up the Development Environment
Bash shells are commonly found on Linux operating systems. In this article, we will be working primarily with Ubuntu, a Linux distribution. You can download and set up Ubuntu here: (Canonical Ubuntu)[http://ubuntu.com/download].
Alternatively, if you're working from a Windows environment, you can download the Windows Subsystem for Linux (WSL) which gives you access to a Linux operating system and a bash terminal, without the need for dual booting, or clean wiping Windows. You can get WSL here: https://learn.microsoft.com/en-us/windows/wsl/install.
Once you have your terminal open, you should see a prompt like the one below:
$
Now we are ready to begin.
Basic Shell commands
-
cd
: Change directory. This command is used to navigate to a different directory within the file system.
$ cd Desktop
# This will switch to the ./Desktop directory.
-
ls
: List directory contents. It displays the files and directories in the current directory.
$ ls
file1.txt
file2.txt
directory1/
-
mkdir
: Make a directory. This command creates a new directory with the given name.
$ mkdir newDir
$ ls
newDir
-
rm
: Remove. It is used to delete files or directories. Be cautious as it does not have a confirmation prompt by default.
$ rm newFile
$ ls
$ rm -rf newDir
$ ls
-
pwd
: Print working directory. It shows the current directory path.
$ pwd
/home/alexin/newDir/
-
cp
: Copy. This command is used to copy files or directories from one location to another.
$ cp ../file_to_copy.txt .
$ ls
file_to_copy.txt
-
mv
: Move. It moves files or directories from one location to another. It can also be used to rename files.
$ mv ../file_to_move.txt .
$ ls
file_to_move.txt
-
touch
: Create a new file. It creates an empty file with the specified name or updates the timestamp of an existing file.
$ touch newFile.txt
$ ls
newFile.txt
-
cat
: Concatenate and display content. This command is used to display the content of files on the terminal. It can also be used to concatenate and display multiple files.
$ cat newFile.txt
This is the content of newFile.txt.
-
echo
: Print text. This is used to print text or variables to the terminal or standard output.
$ echo 'I am Alexin'
I am Alexin
$ name="Alexin"
$ echo 'My name is $name'
My name is Alexin
-
man
: Displays information about a command. 'man' stands for manual, and it is used to provide detailed information about the specified command, including its purpose, syntax, options, and examples of usage.
$ man ls
LS(1) User Commands LS(1)
NAME
ls - list directory contents
SYNOPSIS
ls [OPTION]... [FILE]...
DESCRIPTION
List information about the FILEs (the current directory by default).
Sort entries alphabetically if none of -cftuvSUX nor --sort is speci‐
fied.
Mandatory arguments to long options are mandatory for short options
too.
-a, --all
do not ignore entries starting with .
-A, --almost-all
do not list implied . and ..
--author
Manual page ls(1) line 1 (press h for help or q to quit)
-
grep
: Thegrep
command is used to search for specific patterns or regular expressions within files or streams of text. It stands for "Global Regular Expression Print".
$ grep "error" file.txt
This line contains error.
This line also contains the word "error".
This line has errors (error is in the word errors)
-
find
: Search files and directories. This command lets you search for matching expressions or patterns in a specified file or directory. It allows you to search based on various criteria such as name, type, size, and permissions.
$ find / -
Creating Bash Scripts
A bash script is a file typically ending with the extension .sh
that contains a logical series of related commands to be executed. You can create a bash script using the nano text editor by running this command in your terminal:
$ nano new_script.sh
In the editor, start your script with a shebang line. The shebang line tells the system that this file is a script and specifies the interpreter to use.
#!/bin/bash
You can add a simple command to print the text "Hello, World!" to the terminal.
#!/bin/bash
text="Hello, World!"
echo $text
Save the file then exit: CTRL X
+ Y
+ Enter
.
Before you can run the file, you have to make it executable. Change the file's permissions using the following commands:
$ chmod u+x new_script.sh
Now run your bash script.
$ ./new_script.sh
Hello, World!
Congratulations! You have created your first bash script. Now, let's learn about handling control flow with conditionals and loops.
Control flow
This refers to the order in which commands are executed in a program. In Bash scripting, control flow constructs allow you to manage the execution sequence of your script, enabling you to make decisions, repeat actions, and manage complex logical conditions.
Key Control Flow Constructs in Bash
- Conditional Statements: These are used to execute a block of code only if a specified condition is true.
-
if Statement:
if [ condition ]; then # Code to execute if condition is true fi
-
if-else Statement:
if [ condition ]; then # Code to execute if condition is true else # Code to execute if condition is false fi
-
if-elif-else Statement:
if [ condition1 ]; then # Code to execute if condition1 is true elif [ condition2 ]; then # Code to execute if condition2 is true else # Code to execute if neither condition1 nor condition2 is true fi
- Loops: These are used to repeat a block of code multiple times.
-
for Loop:
for variable in list; do # Code to execute for each item in list done
-
while Loop:
while [ condition ]; do # Code to execute as long as condition is true done
-
until Loop:
until [ condition ]; do # Code to execute until condition is true done
- Case Statement: This is used to execute one of several blocks of code based on the value of a variable.
case $variable in
pattern1)
# Code to execute if variable matches pattern1
;;
pattern2)
# Code to execute if variable matches pattern2
;;
*)
# Code to execute if variable doesn't match any pattern
;;
esac
To demonstrate these concepts, let us write a program that performs a calculation given two numbers and an operation as input:
#!/bin/bash
# Function to perform arithmetic operations
perform_operation() {
# First we define 3 variables, to store the arguments passed into the function.
local num1=$1
local num2=$2
local operation=$3
# We then use a case statement to execute an arithmetic operation, based on the value of the $operation variable. We log the result of the operation to the terminal.
case $operation in
addition)
result=$((num1 + num2))
echo "Result of addition: $result"
;;
subtraction)
result=$((num1 - num2))
echo "Result of subtraction: $result"
;;
multiplication)
result=$((num1 * num2))
echo "Result of multiplication: $result"
;;
division)
if [ $num2 -eq 0 ]; then
echo "Error: Division by zero is not allowed."
else
result=$((num1 / num2))
echo "Result of division: $result"
fi
;;
*)
echo "Invalid operation. Please use one of the following: addition, subtraction, multiplication, division."
;;
esac
}
# Main script starts here
echo "Enter the first number:"
read num1
echo "Enter the second number:"
read num2
echo "Enter the operation (addition, subtraction, multiplication, division):"
read operation
# Perform the operation
perform_operation $num1 $num2 $operation
# Loop to check if user wants to perform another operation
while true; do
echo "Do you want to perform another operation? (yes/no)"
read choice
case $choice in
yes|y|Yes|YES)
echo "Enter the first number:"
read num1
echo "Enter the second number:"
read num2
echo "Enter the operation (addition, subtraction, multiplication, division):"
read operation
# The read command enables a shell script to read user input from the command line.
perform_operation $num1 $num2 $operation
;;
no|n|No|NO)
echo "Exiting the script. Goodbye!"
break
;;
*)
echo "Invalid choice. Please enter yes or no."
;;
esac
done
Handling command line arguments
n a Bash script, command-line arguments are accessed using positional parameters:
-
$0
is the name of the script. -
$1
,$2
, ...,$N
are the arguments passed to the script. -
$#
is the number of arguments passed to the script. -
$@
is all the arguments passed to the script. -
$*
is all the arguments passed to the script as a single word. -
"$@"
is all the arguments passed to the script, individually quoted. -
"$*"
is all the arguments passed to the script, quoted as a single word.
Let's modify the previous arithmetic script to handle command-line arguments. This way, users can specify the numbers and the operation directly when they run the script.
#!/bin/bash
# Function to perform arithmetic operations
perform_operation() {
local num1=$1
local num2=$2
local operation=$3
case $operation in
addition)
result=$((num1 + num2))
echo "Result of addition: $result"
;;
subtraction)
result=$((num1 - num2))
echo "Result of subtraction: $result"
;;
multiplication)
result=$((num1 * num2))
echo "Result of multiplication: $result"
;;
division)
if [ $num2 -eq 0 ]; then
echo "Error: Division by zero is not allowed."
else
result=$((num1 / num2))
echo "Result of division: $result"
fi
;;
*)
echo "Invalid operation. Please use one of the following: addition, subtraction, multiplication, division."
;;
esac
}
if [ $# -ne 3 ]; then
echo "Usage: $0 <num1> <num2> <operation>"
echo "Example: $0 5 3 addition"
exit 1
fi
num1=$1
num2=$2
operation=$3
perform_operation $num1 $num2 $operation
Now that you know the basics of scripting in shell, try out these practice assignments:
Assignment #1: Locate Files Containing the Word "key"
Your task is to write a Bash script that searches for all files containing the word "key" within a specified directory and its subdirectories. The script should:
- Accept the directory path as a command-line argument.
- Use the
grep
command to search for files containing the word "key". - Print the paths of the files that contain the word "key".
Assignment #2: Automate Git Commits for Each Edited File
In this assignment, you will write a Bash script to automate the process of creating a separate Git commit for every file that has been edited in a Git repository. This script will be particularly useful in scenarios where you want to commit each file separately, perhaps for clearer version history or to adhere to specific project guidelines.
- Ensure the script is executed within a Git repository.
- Use Git commands to list files that have been edited.
- Loop through the list of modified files and create a commit for each one.
Conclusion
In summary, this guide has equipped you with the foundational knowledge to navigate the world of Bash scripting. You've explored core concepts like shell commands, creating scripts, control flow structures, and handling user input. With this strong base, you can now venture into more complex scripting tasks to automate various processes and streamline your workflow on Linux systems.
Remember, practice is key to mastering any skill. Experiment with the provided assignments and explore other scripting challenges to solidify your understanding. Thank you for reading!
Top comments (2)
This is just me being picky, but always enclose your variables in curly braces ${variable}, it tends to read better and you get the bonus of shell parameter expansion and array manipulation.
Something like this:
operation=${3:-boguscrapsowealwaysfailifsomeonedoesntpassallthreeinputs}
I would also advise to use something like shellcheck and setting error handling, "set -o", just in case you miss to handle an error.
set -o errexit # abort on nonzero exitstatus
set -o nounset # abort on unbound variable
set -o pipefail # don't hide errors within pipes
or
set -euo pipefail
Good read btw, short and sweet for my ape brain.
I love it. Now I will be able to do HNG devops task 2