Table of Contents
Why Shell Scripting Matters
Shell scripting lets you automate commands you already know how to run interactively in your shell. Instead of typing a sequence of commands over and over, you write them once in a file (a script) and run that file as a program.
A shell script is simply:
- A text file
- Containing shell commands
- Executed by a shell (usually
bashon most Linux distributions)
Shell scripts are especially useful for:
- Repeating regular tasks (backups, cleanups, reports)
- Gluing tools together (e.g., combining
grep,awk,tar) - Running system administration tasks
- Prototyping simple utilities
This chapter stays focused on beginner-level scripting with bash.
The Shebang Line
Most shell scripts start with a shebang line on the first line:
#!/bin/bash#!tells the system: “run this file with the following program”/bin/bashis the path to the Bash shell
Common shebangs you’ll see:
#!/bin/bash # Bash (most common for beginners)
#!/usr/bin/env bash # More portable, finds bash via PATH
#!/bin/sh # POSIX shell (more minimal)If you omit the shebang and run the script like:
bash myscript.sh
then bash will still interpret it, but when you later run ./myscript.sh directly, the shebang becomes important.
Your First Script
Step 1: Create the file
Use any text editor (nano, vim, etc.) to create a file:
nano hello.shAdd this content:
#!/bin/bash
echo "Hello, world!"Save and exit.
Step 2: Make it executable
Scripts are just files until you give execute permission:
chmod +x hello.shNow you can run it with:
./hello.sh
If you see Permission denied, you probably missed the chmod step, or you’re not in the directory with the script.
You can always run a script explicitly with the shell even without chmod +x:
bash hello.shBut making it executable and using the shebang is the usual practice.
Basic Script Structure
A simple bash script often follows this pattern:
#!/bin/bash
# 1. Optional: configuration and variables
BACKUP_DIR="$HOME/backups"
# 2. Optional: simple checks
[ ! -d "$BACKUP_DIR" ] && mkdir -p "$BACKUP_DIR"
# 3. Main commands
echo "Backing up documents..."
cp -r "$HOME/Documents" "$BACKUP_DIR"
echo "Done."Key points:
- Comments start with
#(except in the shebang) and go to the end of the line. - Commands are the same ones you’d type in the terminal.
- Quoting (
"...") is important when filenames may contain spaces.
Running Scripts Safely
Shell scripts can modify or delete files quickly, so get into safe habits early:
- Test with
echofirst
Replace dangerous commands withechoto check what would happen:
# Instead of:
rm "$file"
# First try:
echo rm "$file"- Use absolute paths for important locations
For key directories:
BACKUP_SRC="/home/user/Documents"- Quote variables almost always:
rm "$file" # safer
rm $file # dangerous if $file contains spaces or is empty- Be careful with
sudoinside scripts; prefer running the entire script with proper permissions when needed.
Comments and Readability
Good comments make scripts easier to maintain and understand later.
#!/bin/bash
# Simple backup script for my Documents folder
SRC="$HOME/Documents"
DEST="$HOME/backups"
# Create destination directory if it doesn't exist
mkdir -p "$DEST"
# Copy files (-r = recursive, -v = verbose)
cp -rv "$SRC" "$DEST"Guidelines:
- Explain why you’re doing something, not just what the command is.
- Use comments sparingly but meaningfully (too many can be noise).
Using Commands in Scripts
Any command you use interactively can go into a script. A script is just a list of commands executed top to bottom.
Example: a basic “system summary” script:
#!/bin/bash
echo "System summary for: $(hostname)"
echo
echo "Date and time:"
date
echo
echo "Current user:"
whoami
echo
echo "Uptime:"
uptimeNote:
$(command)runs a command and substitutes its output.- Empty
echolines add blank lines for readability.
Input and Output Basics in Scripts
You can use standard input/output redirection in scripts just like in the terminal.
Example: logging the output of commands to a file:
#!/bin/bash
LOGFILE="$HOME/system-info.log"
{
echo "===== System Info: $(date) ====="
uname -a
echo
df -h
echo
free -h
echo
} >> "$LOGFILE"Here:
{ ...; }groups commands.>> "$LOGFILE"appends all output in the block to a log file.
Redirecting standard output vs standard error:
ls /not/real 1>out.txt 2>err.txtIn scripts you often see:
some_command >>"$LOGFILE" 2>&1This means: append both normal output and errors to the same file.
Getting User Input (Simple)
You can interactively ask the user for input with read:
#!/bin/bash
echo -n "Enter your name: "
read name
echo "Hello, $name!"Or a more idiomatic version:
read -rp "Enter your name: " name
echo "Hello, $name!"-rdisables backslash escaping (usually what you want).-pprints the prompt.
A yes/no question:
read -rp "Do you want to continue? [y/N] " answer
echo "You entered: $answer"(Handling the logic of the answer will be covered in the conditionals chapter.)
Exit Status and `exit`
Every command and script has an exit status, a number where:
0means success- Non-zero means some kind of error
You can view the exit status of the last command with $?:
ls /does/not/exist
echo "Exit status: $?"
To set the exit status of your script, use exit:
#!/bin/bash
if ! command -v rsync >/dev/null 2>&1; then
echo "rsync is not installed."
exit 1
fi
# ... rest of script ...
exit 0 # optional; if omitted, the script's exit status is that of the last commandEven in simple scripts, using different exit codes can help other tools/scripts detect success or failure.
Good Practices for Beginners
Start building good habits early:
- Use a consistent naming convention for your scripts, e.g.
backup-docs.sh,sys-info.sh. - Keep scripts small at first; do one thing well.
- Use
$HOMEinstead of hardcoding/home/usernameto be more portable. - Quote variables unless you specifically want word splitting or globbing:
mv "$old_name" "$new_name"- Keep scripts in a dedicated directory, e.g.
~/binor~/scripts.
You can later add~/binto yourPATHto run them from anywhere.
Example of a small, tidy beginner script:
#!/bin/bash
# Clean the Downloads directory by moving older files to an archive
DOWNLOADS="$HOME/Downloads"
ARCHIVE="$HOME/Downloads/archive"
mkdir -p "$ARCHIVE"
echo "Moving files older than 30 days from $DOWNLOADS to $ARCHIVE..."
find "$DOWNLOADS" -maxdepth 1 -type f -mtime +30 -print -exec mv {} "$ARCHIVE" \;
echo "Done."
(Details of find options would be explained elsewhere; here just notice how a one-liner command becomes reusable through a script.)
Debugging Simple Scripts
When your script doesn’t behave as expected, a few basic techniques help:
- Add
echostatements to see values:
echo "DEBUG: backup directory is '$BACKUP_DIR'"- Run the script with tracing enabled:
bash -x myscript.shThis prints each command as it’s executed.
- Or add this near the top of the script:
set -x # turn on tracing
# ... commands ...
set +x # turn off tracingStart with these before moving to more advanced debugging tools.
Where to Go Next
Once you’re comfortable with creating and running simple scripts:
- Variables: store, expand, and manipulate data inside scripts.
- Conditionals: run commands only when certain conditions are true.
- Loops: repeat actions over lists of files or values.
- Functions: group reusable code inside your scripts.
- Making scripts executable (in depth): permissions,
PATH, and conventions.
Each of these topics will be explored in the following sections of this shell scripting module.