Introduction
Hello! Welcome to the final lesson in our Bash scripting module. Over the last several lessons, you've assembled a powerful toolkit: you've learned to use variables, write functions, control logic with if statements, iterate with loops, parse command-line arguments, and, most recently, make your scripts robust by handling errors and exit codes.
Today, we will bring all of these skills together to achieve this lesson's goal: to write a practical automation script to back up a specified project directory. This isn't just a theoretical exercise; it's a foundational task for any system administrator or web developer. As you progress with your Django projects, you'll need a reliable way to create backups before deploying new code, migrating a database, or moving your project to a new server. A well-written backup script is your safety net.
We'll focus on using rsync, a powerful and efficient tool for synchronizing files. By the end of this lesson, you will have built a reusable, real-world backup script that incorporates the best practices you've learned throughout this module.
1. The Anatomy of a Professional Backup Script
A good backup script does more than just copy files. It should be smart, safe, and informative. The best way to understand these principles is to see a professional script being built and explained.
We are going to watch a comprehensive video that walks through the creation of a backup script using rsync. This script is an excellent model because it includes several features we've discussed, such as argument validation and dependency checking, and introduces some powerful rsync-specific features.
How To Write Bash Scripts In Linux - Complete Guide (Part 17 - Backup Script)
This video, "How To Write Bash Scripts In Linux - Complete Guide (Part 17 - Backup Script)" from Learn Linux TV, is a masterclass in building a practical script. It will serve as our main guide for this lesson.
Please watch the entire video (from 00:48 to the end). As you watch, focus on how the script is constructed piece by piece: Argument & Dependency Checks (02:08): Notice how if statements are used to ensure the user provides the correct number of arguments and that rsync is installed. This makes the script user-friendly and robust. Date-Stamping (04:19): See how the date command is used with command substitution to create dynamically named directories. This is key for organizing backups over time. Key rsync Options (05:15): Pay close attention to the explanation of the rsync options, especially: -a (archive), -v (verbose), --delete --backup and --backup-dir: This combination creates a powerful incremental backup system. --dry-run: This is the most important option for testing. It simulates the backup without changing any files. Logging (11:57): Understand why the script's output is redirected to a date-stamped log file for auditing purposes. Testing and Execution (14:20): Observe the complete workflow: testing with --dry-run, fixing an error, running the script for real, and then verifying the results.
This video demonstrates a key principle of system administration: automation should be predictable and verifiable. The use of --dry-run and log files isn't optional; it's essential for trusting your scripts.
2. Two Approaches to Backups: rsync vs. tar
The video focuses on rsync, which excels at synchronizing directories. It's incredibly efficient because, after the first run, it only transfers the parts of files that have changed. This makes it ideal for frequent, automated backups of a project that is actively being developed. The incremental backup strategy shown in the video, where old versions of files are moved to dated folders, is a perfect example of rsync's power.
Another common tool is tar, which stands for "tape archive." Its purpose is to bundle many files and directories into a single archive file (e.g., project.tar.gz). This is useful when you want to create a single, portable snapshot of your project, perhaps to download it or send it to someone.
For your goal of managing a live Django project, rsync is generally the more powerful and efficient tool for regular backups on your server.
If you're interested in seeing how a tar-based script is built, the "Bash Scripting Tutorial for Beginners" article provides a great step-by-step guide. It evolves a simple script into a more complex one using many of the techniques we've covered. Consider this optional further reading.
Bash Scripting Tutorial for Beginners
For an alternative perspective, this article from linuxconfig.org builds a backup script using tar. You can see how it evolves to include variables, functions, and error checking.
This is optional. Skim through the sections starting from "Simple Backup Bash Shell Script" to the end. You don't need to study it in depth, but notice how the backup.sh script is gradually improved in each section.
Do-It-Yourself: Backing Up Your Django Project
Now it's time to build your own backup script, applying the best practices from the Learn Linux TV video. We will create a script named backup_project.sh to back up a mock Django project directory.
Your Goal: Create a script that takes a source directory and a destination directory as arguments and uses rsync to create an incremental backup, complete with logging and a "dry run" mode for safety.
Instructions:
-
Set up your environment:
First, let's create a mock Django project to work with. Open your terminal and run these commands:cd ~ # Create the project structure mkdir -p django_projects/my_site/static/css # Create some dummy files touch django_projects/my_site/manage.py touch django_projects/my_site/my_site/settings.py touch django_projects/my_site/my_site/urls.py echo "body { color: blue; }" > django_projects/my_site/static/css/style.css # Create a directory to store your scripts mkdir -p ~/automation_scripts cd ~/automation_scripts -
Create the Script File:
Create a new file namedbackup_project.shand make it executable.touch backup_project.sh chmod +x backup_project.sh -
Write the Script:
Openbackup_project.shin your preferred text editor (likevim,nano, or VS Code) and build the script step-by-step. Use the code from theLearn Linux TVvideo and the concepts from our previous lessons as your guide.Your script should include:
- The shebang line and "strict mode" (
set -euo pipefail). - Argument validation to ensure exactly two arguments (source and destination) are provided. If not, print a usage message and exit with an error code.
- A check to ensure
rsyncis installed. If not, print an error and exit. - A variable to hold the current date in
YYYY-MM-DDformat. - A variable to hold the
rsyncoptions. Crucially, start with--dry-runincluded in your options! - The main
rsynccommand that:- Uses the options you defined.
- Takes the source (
$1) and destination ($2) from the command line. - Implements the incremental backup strategy (
--backup --backup-dir=...). - Redirects all output to a log file in the destination directory, named with the current date (e.g.,
backup-2024-10-27.log).
- The shebang line and "strict mode" (
-
Test with Dry Run:
Now, run your script to back up the mock project. We'll create abackupsdirectory in your home folder to store the backups.# Make sure you are in ~/automation_scripts mkdir -p ~/backups ./backup_project.sh ~/django_projects/my_site ~/backups- Check the contents of the log file (
~/backups/backup-YYYY-MM-DD.log). You should see a list of files that would have been copied and a "dry run" message at the end. - Verify that the
~/backupsdirectory is still empty (except for the log file), because it was just a simulation.
- Check the contents of the log file (
-
Perform the Real Backup:
Once you're confident the script is behaving correctly, editbackup_project.shand remove the--dry-runoption. Save the file.Now, run the script again:
./backup_project.sh ~/django_projects/my_site ~/backups- Check the
~/backupsdirectory. You should now see acurrentdirectory containing your full project. - Examine the log file again. It should show the files being copied, but without the "dry run" message.
- Check the
-
Test the Incremental Backup:
Let's simulate a change to the project and run the backup again.# Modify a file in the source project echo "/* A new comment */" >> ~/django_projects/my_site/static/css/style.css # Run the backup again ./backup_project.sh ~/django_projects/my_site ~/backups- Check the log file. It should now only list the
style.cssfile that changed. - Inspect the
~/backupsdirectory. You will find a new date-stamped directory (e.g.,2024-10-27). Inside it will be the previous version ofstyle.css. The~/backups/currentdirectory will contain the newest version.
- Check the log file. It should now only list the
You have successfully created and tested a robust, incremental backup script!
Click here to see a complete example of the `backup_project.sh` script.
#!/usr/bin/env bash
# Exit immediately if a command exits with a non-zero status.
# Treat unset variables as an error.
# The return value of a pipeline is the status of the last command to exit
# with a non-zero status, or zero if no command exited with a non-zero status.
set -euo pipefail
# --- Argument Validation ---
if [[ "$#" -ne 2 ]]; then
echo "Usage: $0 <source_directory> <destination_directory>"
echo "Example: $0 ~/django_projects/my_site ~/backups"
exit 1
fi
SOURCE_DIR="$1"
DEST_DIR="$2"
# --- Dependency Check ---
if ! command -v rsync &> /dev/null; then
echo "Error: rsync is not installed. Please install it to continue." >&2
exit 2
fi
# --- Main Logic ---
CURRENT_DATE=$(date "+%Y-%m-%d")
LOG_FILE="${DEST_DIR}/backup-${CURRENT_DATE}.log"
# Use --archive for permissions, --verbose for output, --backup and --backup-dir for incrementals,
# --delete to remove files in dest that are gone from source.
# Start with --dry-run for testing!
RSYNC_OPTS=(
--archive
--verbose
--backup
--backup-dir="${DEST_DIR}/${CURRENT_DATE}"
--delete
# --dry-run # <--- Comment this out or remove it to perform a real backup
)
echo "Starting backup of ${SOURCE_DIR} to ${DEST_DIR} on ${CURRENT_DATE}" | tee -a "$LOG_FILE"
# Create destination directory if it doesn't exist
mkdir -p "$DEST_DIR"
# Run rsync and append its output (stdout and stderr) to the log file
# The 'tee -a' command above already prints the start message to the log
rsync "${RSYNC_OPTS[@]}" "${SOURCE_DIR}/" "${DEST_DIR}/current/" >> "$LOG_FILE" 2>&1
echo "Backup finished. Log available at ${LOG_FILE}"
Note: This example uses a Bash array RSYNC_OPTS=(...) to hold the options. This is a robust way to handle arguments with spaces. The tee -a command is used to print the starting message to both the screen and the log file.
Conclusion
Congratulations on completing the Bash scripting module! You have successfully built a practical, powerful, and robust automation script that directly supports your goals as a future Full Stack Developer. This backup script is not just an exercise; it's a real tool you can adapt and use for your projects.
Key Takeaways:
- A great backup script is more than a copy command. It includes argument validation, dependency checks, and clear logging.
rsyncis the industry standard for creating efficient, incremental backups.- The
--dry-runoption is your most important safety feature. Always test with a dry run before executing a command that modifies or deletes files. - Organizing backups with date-stamped directories for logs and changed files creates a clear and auditable history.
You have now mastered the fundamental building blocks of shell scripting. This knowledge will be invaluable as you automate deployment tasks, manage servers, and build complex applications.
In our next module, User and Group Administration, we'll shift our focus to managing who can access your system and what they are allowed to do. We'll start by learning how to create, modify, and delete user accounts. This is a critical skill for securing your development and production environments, as you'll often want to run services like your Django application or a database under dedicated, non-root user accounts.