Introduction
Bash scripting is one of the most useful skills a Raspberry Pi user can learn. A Raspberry Pi is often used for small servers, automation projects, data logging, device control, scheduled tasks, and system maintenance. In almost all of those situations, you eventually repeat the same terminal commands again and again. Bash scripting solves that problem by letting you place those commands into a file and run them as a single program.
On a Raspberry Pi, that can save time and reduce mistakes. Instead of typing a long update command, checking disk space manually, restarting services one by one, or copying files with repeated options, you can create a script that does the work for you. Even a very small script can make a Raspberry Pi easier to manage.
Bash scripts are especially helpful because Raspberry Pi OS is Linux-based, and Linux systems are built around the shell. Once you understand a few Bash basics, you can automate common jobs, connect commands together, work with files and folders more efficiently, and build simple but powerful tools for your own projects.
This article explains the foundations of Bash scripting for Raspberry Pi in a practical way. The goal is not to turn Bash into a programming language for huge applications. The goal is to help you become comfortable writing useful scripts for real Pi tasks.
What Bash is
Bash is a shell. A shell is a program that accepts commands and runs them. When you open a terminal on a Raspberry Pi and type commands such as ls, cd, pwd, or sudo apt update, you are interacting with a shell. Bash is one of the most common shells on Linux systems.
A Bash script is simply a text file containing shell commands. Instead of running commands one at a time by hand, you save them into a file and run the file. The shell reads the commands in order and executes them.
That simplicity is one of the reasons Bash is so useful. You are not starting from zero when you learn Bash scripting. If you already use the terminal, you already know part of the language. A script is just a structured way to group terminal commands together.
Why Bash scripting is useful on Raspberry Pi
A Raspberry Pi is often left running for long periods and used for repeated tasks. That makes scripting especially valuable.
Imagine that you use your Pi as a small server. You may want a script that updates the system, clears old logs, checks disk space, and restarts a service. If your Pi is part of a sensor project, you may want a script that launches your program, stores output in a log file, and checks whether a device is connected first. If your Pi is a learning machine or desktop system, you may want a script that backs up files to a USB drive.
These are all situations where Bash scripting shines. It is fast to write, easy to run, and already built into the system. You do not need a special compiler. You do not need to install a large development environment. You only need a text editor and a terminal.
Creating your first Bash script
Let us start with the smallest useful example.
Open a terminal and create a file:
nano hello.sh
Type this into the file:
#!/bin/bash
echo "Hello from Raspberry Pi"
Save the file and exit. This file is now a Bash script.
The first line is called the shebang. It tells the system which interpreter should run the script. In this case, it tells Linux to use Bash.
The second line uses echo to print text to the terminal.
Before running the script directly, make it executable:
chmod +x hello.sh
Now run it:
./hello.sh
You should see:
Hello from Raspberry Pi
That is your first Bash script.
Running a script in different ways
There are two common ways to run a Bash script.
The first is to make it executable and run it directly:
./hello.sh
The second is to pass it to Bash explicitly:
bash hello.sh
Both approaches work, but making it executable is often more convenient once the script is ready.
If you try to run ./hello.sh and get a permission error, it usually means the file is not marked as executable. That is why chmod +x is important.
Comments in Bash scripts
Comments are notes for humans. Bash ignores them when the script runs. A comment starts with #.
Example:
#!/bin/bash
# This script prints a message
echo "Starting script"
Comments are useful for explaining what a script does, reminding yourself why a command exists, or separating one part of a script from another. Even in small scripts, comments can make maintenance much easier later.
Variables
Variables let you store values and use them later in the script. In Bash, you assign a variable without spaces around the equals sign.
Example:
#!/bin/bash
name="Pi User"
echo "Hello, $name"
This prints:
Hello, Pi User
The $name part means “use the value stored in the variable called name.”
You can store many kinds of simple values in variables, such as names, file paths, dates, and command output.
Example:
#!/bin/bash
folder="/home/pi/projects"
echo "Project folder is $folder"
Variable names should be clear and simple. Good variable names make a script easier to understand.
Reading user input
A script can ask the user for input with read.
Example:
#!/bin/bash
echo "What is your name?"
read name
echo "Nice to meet you, $name"
When the script runs, it pauses and waits for the user to type something. Whatever the user types is stored in the variable.
You can also combine the prompt and the input command:
#!/bin/bash
read -p "Enter your project name: " project
echo "Project is $project"
This is useful when you want a script to feel interactive.
Using commands inside variables
Sometimes you want to store the result of a command. You can do that with command substitution.
Example:
#!/bin/bash
current_date=$(date)
echo "Current date and time: $current_date"
The $(date) part runs the date command and stores its output in the variable.
This is very common in Raspberry Pi scripts. You might store the current date for log files, check system uptime, or capture disk usage.
Example:
#!/bin/bash
uptime_info=$(uptime)
echo "System uptime: $uptime_info"
Working with arguments
Scripts can accept command-line arguments. These are values passed when the script is run.
Example script:
#!/bin/bash
echo "First argument: $1"
echo "Second argument: $2"
If you save this as args.sh and run:
./args.sh apple banana
You will get:
First argument: apple
Second argument: banana
The special variables $1, $2, and so on represent arguments. This is useful when you want to write flexible scripts. For example, a backup script might accept a source folder and a destination folder.
There is also $#, which tells you how many arguments were provided.
Example:
#!/bin/bash
echo "Number of arguments: $#"
This can help a script check whether it has enough information to continue.
Basic if statements
An if statement lets a script make decisions.
Example:
#!/bin/bash
read -p "Enter your age: " age
if [ "$age" -ge 18 ]; then
echo "You are an adult."
fi
This checks whether the age is greater than or equal to 18. If it is, the message is printed.
A more complete example includes else:
#!/bin/bash
read -p "Enter your age: " age
if [ "$age" -ge 18 ]; then
echo "You are an adult."
else
echo "You are under 18."
fi
This is useful for scripts that need to act differently depending on input, file existence, device status, or command results.
Comparing values
In Bash, comparisons depend on whether you are working with numbers or strings.
For numbers:
-eq equal
-ne not equal
-gt greater than
-lt less than
-ge greater than or equal
-le less than or equal
Example:
#!/bin/bash
value=10
if [ "$value" -gt 5 ]; then
echo "Value is greater than 5"
fi
For strings:
= equal
!= not equal
Example:
#!/bin/bash
name="pi"
if [ "$name" = "pi" ]; then
echo "Welcome, pi"
fi
Always be careful with spaces. In Bash tests, spaces matter. [ "$name" = "pi" ] is correct. ["$name"="pi"] is not.
Checking files and directories
A Raspberry Pi script often needs to work with files, directories, or devices. Bash has built-in test expressions for this.
To check whether a file exists:
#!/bin/bash
if [ -f /home/pi/test.txt ]; then
echo "File exists"
else
echo "File not found"
fi
To check whether a directory exists:
#!/bin/bash
if [ -d /home/pi/projects ]; then
echo "Directory exists"
else
echo "Directory not found"
fi
This is extremely useful in Raspberry Pi automation. A script can check whether a USB drive is mounted, whether a config file is present, or whether a backup folder already exists.
Exit status and why it matters
Every command returns an exit status. A status of 0 usually means success. A non-zero value usually means something went wrong.
Bash scripts can use this behavior to decide what to do next.
Example:
#!/bin/bash
mkdir /tmp/mytestfolder
if [ $? -eq 0 ]; then
echo "Folder created successfully"
else
echo "Failed to create folder"
fi
The special variable $? contains the exit status of the most recent command.
A more natural style often checks the command directly:
#!/bin/bash
if mkdir /tmp/mytestfolder; then
echo "Folder created successfully"
else
echo "Failed to create folder"
fi
This is cleaner and easier to read.
Loops
Loops let a script repeat actions. Bash supports several loop types, but the most common for beginners are for and while.
For loop
A for loop is useful when you have a list of values.
Example:
#!/bin/bash
for fruit in apple banana orange
do
echo "Fruit: $fruit"
done
This prints each fruit one by one.
A Raspberry Pi example might loop through files:
#!/bin/bash
for file in /home/pi/*.txt
do
echo "Found file: $file"
done
That can be useful for batch processing logs, configuration files, or sensor output.
While loop
A while loop repeats while a condition remains true.
Example:
#!/bin/bash
count=1
while [ "$count" -le 5 ]
do
echo "Count is $count"
count=$((count + 1))
done
This prints numbers from 1 to 5.
A while loop can be helpful when monitoring a service, retrying an operation, or waiting for a condition to change.
Arithmetic in Bash
Bash can do simple arithmetic with $(( )).
Example:
#!/bin/bash
a=5
b=3
sum=$((a + b))
echo "Sum is $sum"
You can also increment counters:
#!/bin/bash
count=0
count=$((count + 1))
echo "$count"
This is often used in loops, retries, and simple calculations inside automation scripts.
Functions
Functions let you group code into reusable blocks. This makes scripts easier to organize.
Example:
#!/bin/bash
say_hello() {
echo "Hello from function"
}
say_hello
A function can be called multiple times, which is helpful if the same action is needed in different parts of the script.
Example:
#!/bin/bash
show_time() {
echo "Current time: $(date)"
}
show_time
sleep 2
show_time
In a Raspberry Pi project, you might create functions for checking Wi-Fi, starting a sensor, writing to a log, or cleaning temp files.
Useful Bash commands in Raspberry Pi scripts
Many terminal commands are especially useful in scripts. A few examples include:
echo prints text.
pwd shows the current directory.
cd changes directory.
mkdir creates a directory.
cp copies files.
mv moves or renames files.
rm removes files.
cat displays file contents.
grep searches for text.
date gets the current date and time.
uptime shows system uptime.
df shows disk usage.
free shows memory usage.
systemctl manages services.
These commands become much more powerful when combined in scripts.
Example: system info script
Here is a simple Raspberry Pi Bash script that displays useful system information:
#!/bin/bash
echo "Raspberry Pi System Information"
echo "-------------------------------"
echo "Hostname: $(hostname)"
echo "Date: $(date)"
echo "Uptime: $(uptime -p)"
echo "Disk usage:"
df -h /
echo "Memory usage:"
free -h
This is a good example of a script that turns several manual commands into one reusable tool.
Example: update script
A practical Raspberry Pi script might update the system:
#!/bin/bash
echo "Updating package list..."
sudo apt update
echo "Upgrading packages..."
sudo apt upgrade -y
echo "System update complete."
This is simple, but useful. It saves typing and ensures you always run the steps in the same order.
Example: backup script
Backups are a great Bash scripting project on Raspberry Pi.
#!/bin/bash
source_folder="/home/pi/projects"
backup_folder="/home/pi/backups"
timestamp=$(date +"%Y-%m-%d_%H-%M-%S")
mkdir -p "$backup_folder"
cp -r "$source_folder" "$backup_folder/projects_backup_$timestamp"
echo "Backup completed at $timestamp"
This script creates a timestamped backup folder. It is a practical example of variables, command substitution, directory creation, and file copying.
Logging output
It is often useful to save script output to a file. This is called logging.
Example:
#!/bin/bash
log_file="/home/pi/myscript.log"
echo "Script started at $(date)" >> "$log_file"
echo "Doing work..." >> "$log_file"
echo "Script finished at $(date)" >> "$log_file"
The >> operator appends text to a file instead of overwriting it.
This is extremely useful for Raspberry Pi projects that run unattended. If something goes wrong, the log file may help explain what happened.
Redirecting output
Bash can redirect output in several ways.
> writes output to a file and replaces its current contents.
>> appends output to a file.
Example:
echo "Hello" > output.txt
echo "World" >> output.txt
This produces a file containing:
Hello
World
You can also redirect errors. Standard output and standard error are separate streams. For beginners, the most important point is that scripts can store messages in files instead of only showing them on the screen.
Using pipes
A pipe sends the output of one command into another command.
Example:
df -h | grep "/$"
This takes the output of df -h and passes it to grep, which filters the result.
Pipes are a big part of Bash power. On Raspberry Pi, they are often used for filtering logs, checking process lists, reading sensor command output, or extracting specific system information.
Scheduling scripts with cron
One of the most useful Raspberry Pi scripting skills is running scripts automatically. The standard tool for this is cron.
To edit your cron jobs:
crontab -e
A cron entry looks like this:
0 8 * * * /home/pi/backup.sh
This means the script runs every day at 8:00 AM.
This turns Bash scripts into automation tools. A Raspberry Pi can update logs every hour, back up files every night, or restart a service every morning without manual action.
If you use cron, make sure your script uses full file paths. A script that works in the terminal may fail in cron if it depends on the current directory or missing environment settings.
Common beginner mistakes
One of the most common mistakes is forgetting to make the script executable. If ./script.sh does not work, check file permissions.
Another common mistake is incorrect spacing. Bash is very sensitive to spaces, especially in if statements and variable assignments.
This is correct:
name="Pi"
if [ "$name" = "Pi" ]; then
echo "Match"
fi
This is incorrect:
name = "Pi"
if ["$name"="Pi"]; then
echo "Match"
fi
Another mistake is forgetting to quote variables. Quotes help prevent problems when values contain spaces.
Example:
cp "$source_file" "$destination_file"
Without quotes, file paths with spaces can break the script.
A further mistake is testing scripts only once. If a script will be used often, test it with different inputs and situations. Try missing files, bad arguments, or full directories. Small scripts can still fail in unexpected ways.
Making scripts safer
As you grow more comfortable, it is worth making scripts safer and more predictable.
A good habit is to check whether important files or folders exist before using them.
Another good habit is to print clear messages so you know what the script is doing. A silent script can be hard to debug.
You can also stop a script when a command fails by using stricter options, but even without advanced techniques, careful checks and good messages make a big difference.
For example:
#!/bin/bash
backup_folder="/home/pi/backups"
if [ ! -d "$backup_folder" ]; then
echo "Backup folder does not exist"
exit 1
fi
echo "Backup folder found"
This is much safer than assuming everything is always ready.
A practical Raspberry Pi project example
Suppose you have a Raspberry Pi running a small Python sensor script, and you want a Bash script to launch it, log the start time, and make sure the project folder exists.
Example:
#!/bin/bash
project_dir="/home/pi/sensor_project"
log_file="/home/pi/sensor_project/run.log"
if [ ! -d "$project_dir" ]; then
echo "Project directory not found: $project_dir"
exit 1
fi
echo "Starting sensor script at $(date)" >> "$log_file"
cd "$project_dir" || exit 1
python3 sensor.py >> "$log_file" 2>&1
echo "Sensor script finished at $(date)" >> "$log_file"
This script shows the real value of Bash on Raspberry Pi. It checks conditions, uses variables, changes directory, runs another program, and writes logs. This kind of wrapper script is very common in real projects.
When Bash is the right choice
Bash is excellent for system automation, file handling, command chaining, startup scripts, maintenance tasks, and lightweight project control. On Raspberry Pi, that covers a lot of common use cases.
However, Bash is not always the best choice for everything. If you need complex data structures, advanced networking logic, or larger applications, Python may be a better fit. Many Raspberry Pi users combine both: Bash for automation and launching tasks, Python for the main application logic.
That is a strong approach. Use Bash where the shell is naturally powerful, and use Python where a full programming language is easier to manage.
Conclusion
Bash scripting is one of the most practical skills you can learn for Raspberry Pi. It helps you automate tasks, organize commands, create reusable tools, and manage your Pi more efficiently. The key ideas are simple: create a text file, add Bash commands, use variables and conditions, make the file executable, and run it.