Getting Started with Bash
🐚 Topic 1 — Getting Started with Bash
Before writing a single line of code, it helps to understand what Bash actually is, how it relates to your terminal, and the basic mechanics of creating and running a script. This chapter covers all of that — by the end you will have written and executed your first working script and understand exactly what happened when you ran it.
1 — What is the Shell?
When you open a terminal on a Linux system, you are not talking directly to the operating system kernel. Between you and the kernel sits a programme called a shell — a command interpreter that reads what you type, works out what you mean, and asks the kernel to carry it out.
sh) that it replaced and extended.Available Shells
| Shell | Full Name | Notes |
|---|---|---|
bash | Bourne Again Shell | Default on most Linux distros. The focus of this course. |
sh | Bourne Shell (or POSIX sh) | The original. On modern systems /bin/sh is usually a link to dash or bash in POSIX mode. Fewer features than bash. |
zsh | Z Shell | Default on macOS since Catalina. Very similar to bash with extra features. |
fish | Friendly Interactive Shell | User-friendly with autosuggestions but not POSIX-compatible. |
dash | Debian Almquist Shell | Lightweight and fast. Ubuntu uses it as /bin/sh for system scripts. |
# Print your current shell
echo $SHELL
/bin/bash
# Print the exact version of bash installed
bash --version
GNU bash, version 5.2.21(1)-release (x86_64-pc-linux-gnu)
# List all shells installed on the system
cat /etc/shells
2 — What is a Shell Script?
A shell script is nothing more than a plain text file containing a sequence of commands — exactly the same commands you would type at the terminal, one per line. Instead of typing them one at a time, you write them all in a file and tell the shell to run the file. This lets you automate repetitive tasks, combine commands into reusable tools, and build complex workflows.
hello.sh.#!/bin/bash
# My first bash script
echo "Hello, World!"
echo "Today is: "
date
That is a complete, working script. The following sections explain each part of it.
3 — The Shebang Line
The very first line of a script is special. The two characters #! (called a shebang or hashbang) followed by a path tell the operating system which interpreter to use to run this file.
#!/bin/bash
When the kernel sees a file starting with #!, it hands the file to the programme named after the !. In this case it runs /bin/bash and passes the script to it as input.
#!/bin/bash # use bash explicitly
#!/usr/bin/env bash # find bash via PATH (more portable)
#!/bin/sh # use the system's POSIX shell (dash on Ubuntu)
#!/usr/bin/env python3 # same mechanism works for Python scripts
#!/bin/bash when you want bash-specific features (arrays, [[ ]], process substitution). Use #!/usr/bin/env bash when you need portability across systems where bash may not be in /bin. Use #!/bin/sh only when you intentionally want POSIX-only compatibility.
4 — Making a Script Executable
A newly created text file is not executable by default. Linux uses a permissions system to control who can read, write, and execute files. Before you can run a script with ./script.sh, you must grant it execute permission.
# Create the script file
nano hello.sh
# Check the current permissions
ls -l hello.sh
-rw-r--r-- 1 philip philip 62 Jun 9 10:00 hello.sh
# rw-r--r-- = owner can read/write, others can only read. Nobody can execute.
# Grant execute permission to the owner
chmod +x hello.sh
# Verify the permission change
ls -l hello.sh
-rwxr-xr-x 1 philip philip 62 Jun 9 10:00 hello.sh
# The 'x' bits confirm execute permission is now set.
Understanding Permission Notation
| Characters | Who | Meaning |
|---|---|---|
rwx | Owner (you) | Read, Write, Execute |
r-x | Group | Read, no Write, Execute |
r-x | Others (everyone else) | Read, no Write, Execute |
chmod +x adds execute permission for everyone (owner, group, others). chmod 755 does the same but also sets read/write for owner and read-only for everyone else. For personal scripts, chmod +x is fine. For scripts shared across users, chmod 755 is more explicit.
5 — Ways to Run a Script
There are three main ways to run a bash script, each with slightly different behaviour:
# ── Method 1: Direct execution (requires chmod +x) ────────────────
./hello.sh
# The ./ means "in the current directory". The kernel reads the shebang
# and launches /bin/bash to run the script.
# ── Method 2: Call bash explicitly (no chmod needed) ──────────────
bash hello.sh
# Tells bash directly to interpret the file. The shebang line is ignored
# (it becomes just a comment) because you specified the interpreter.
# ── Method 3: Source the script (dot command) ─────────────────────
source hello.sh
# or equivalently:
. hello.sh
# Runs the script in the CURRENT shell session, not a new child process.
# Variables and functions defined in the script persist after it finishes.
# Useful for scripts that set environment variables (e.g. .bashrc).
| Method | New process? | Needs chmod +x? | Best used for |
|---|---|---|---|
./script.sh | Yes | Yes | Normal script execution |
bash script.sh | Yes | No | Quick testing, debugging |
source script.sh | No | No | Scripts that set variables/aliases |
PATH for security reasons. Without ./, the shell would search your PATH for a command named hello.sh and find nothing. The ./ prefix explicitly says "look in the current directory".
6 — Comments
A comment is text in a script that the shell ignores completely. Comments exist purely for the human reader. In bash, anything from a # character to the end of the line is a comment — except for the shebang on line 1.
#!/bin/bash
# ─────────────────────────────────────────────────────
# Script: backup.sh
# Purpose: Creates a compressed backup of a directory
# Author: Philip
# Date: 2026-06-09
# ─────────────────────────────────────────────────────
echo "Starting backup..." # inline comment — comes after code
# The next line creates the archive
tar -czf backup.tar.gz /home/philip/documents
# increment counter (obvious), write # skip the header line in the CSV (explains purpose).
7 — Script Structure and the PATH
Typical Script Layout
#!/bin/bash
# ─────────────────────────────────────────────
# Script name and one-line description
# ─────────────────────────────────────────────
# ── Configuration / constants ──────────────
LOG_FILE="/var/log/myscript.log"
MAX_RETRIES=3
# ── Functions ──────────────────────────────
greet() {
echo "Hello, $1!"
}
# ── Main logic ─────────────────────────────
greet "World"
echo "Script complete."
Adding Scripts to your PATH
Once you have a collection of scripts you use regularly, you can place them in a directory and add that directory to your PATH so you can run them from anywhere without typing ./.
# Create a personal scripts directory
mkdir -p ~/bin
# Copy your script there
cp hello.sh ~/bin/hello
# Add ~/bin to PATH permanently by adding this line to ~/.bashrc
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
# Apply the change in the current session
source ~/.bashrc
# Now you can run the script from anywhere
hello
Hello, World!
echo $PATH first.8 — A First Look at Debugging
Even in a simple script, things can go wrong. Bash has a built-in debug mode that prints each command before executing it — invaluable when a script isn't behaving as expected.
# Run with -x to see every command as it executes
bash -x hello.sh
+ echo 'Hello, World!'
Hello, World!
+ echo 'Today is: '
Today is:
+ date
Mon Jun 9 10:15:32 BST 2026
# Or add set -x inside the script to enable debug mode from that point
#!/bin/bash
set -x # turn debug on
echo "hello"
set +x # turn debug off
+ prefix in debug output marks commands executed by bash. We cover error handling and debugging in depth in Topic 11.✏️ Exercises
Apply what you have learned in this chapter. Try each exercise yourself before looking at the sample solution.
sysinfo.sh that prints the following on separate lines: the current date and time, your username, your home directory, and the hostname of the machine.date, whoami, echo $HOME, and hostname will each give you one of the pieces of information you need.#!/bin/bash
# sysinfo.sh — prints basic system information
echo "Date and time : $(date)"
echo "Username : $(whoami)"
echo "Home directory: $HOME"
echo "Hostname : $(hostname)"
The $( ) syntax is called command substitution — it runs the command inside and inserts its output into the string. We cover this in more detail in Topic 3.
greet.sh that accepts a name as a command-line argument and prints Hello, [name]!. If no argument is provided, it should print Hello, World! instead.$1 holds the first argument passed to the script. You can check whether it is empty with if [ -z "$1" ].#!/bin/bash
# greet.sh — greets a named person, or the world
if [ -z "$1" ]; then
echo "Hello, World!"
else
echo "Hello, $1!"
fi
Don't worry if the if syntax looks unfamiliar — conditionals are covered fully in Topic 5. The important concept here is $1 for the first argument.
setup_project.sh that creates a project directory structure. It should create a directory called my_project containing three subdirectories: src, docs, and tests. It should then print a confirmation message listing each directory created.mkdir -p creates a directory and any missing parent directories in one command.#!/bin/bash
# setup_project.sh — creates a standard project layout
mkdir -p my_project/src
mkdir -p my_project/docs
mkdir -p my_project/tests
echo "Project structure created:"
echo " my_project/"
echo " my_project/src"
echo " my_project/docs"
echo " my_project/tests"
Bonus: Try rewriting this using a loop over an array of directory names — something to revisit after Topics 6 and 8!
mypath.sh that prints each directory in your PATH environment variable on its own line, with a line number in front of each one. For example: 1: /usr/local/bin, 2: /usr/bin, etc.$PATH contains directories separated by colons. You can split it by replacing : with newlines using echo "$PATH" | tr ':' '\n'. Piping through a loop with a counter will let you add line numbers.#!/bin/bash
# mypath.sh — lists PATH directories with line numbers
count=1
echo "$PATH" | tr ':' '\n' | while read -r dir; do
echo "$count: $dir"
count=$(( count + 1 ))
done
This exercise previews pipes, loops, and arithmetic — all covered in upcoming topics. If the solution looks complex now, revisit it after Topics 3 and 6.