Shell Scripting for Beginners: A Complete Guide
Shell scripting is one of the most powerful skills you can learn as a developer, system administrator, or DevOps engineer. It bridges the gap between manual system operations and full automation, allowing you to write programs that interact directly with your operating system.
In this guide, we'll explore what shell scripting is, why it matters, and how to write effective scripts that save time and reduce human error.
What Is Shell Scripting?
A shell script is a plain text file containing a sequence of commands that a Unix-based operating system can execute. Think of it as a recipe: instead of typing commands one by one in the terminal, you write them in a file and run that file as a program.
The shell is the command-line interpreter that reads your script and translates those commands into instructions for the computer. When you run a script, the shell executes each line sequentially, just as if you had typed them manually.
A Simple Example
Create a file called hello.sh:
1#!/bin/bash 2 3# This is a comment 4echo "Hello, World!" 5echo "Today is $(date +%A, %B %d, %Y)" 6echo "You are logged in as: $(whoami)"
Make it executable and run it:
1chmod +x hello.sh 2./hello.sh
Output:
Hello, World!
Today is Sunday, August 16, 2026
You are logged in as: ubuntu
The first line #!/bin/bash is called a shebang. It tells the system which interpreter (in this case, Bash) should execute the script.
Why Learn Shell Scripting?
Shell scripting isn't just a "nice to have" skill — it's foundational for anyone working with servers, cloud infrastructure, or development pipelines.
1. Automation
Repetitive tasks are where shell scripts shine. Instead of manually performing the same operations every day, you schedule a script to do it for you.
Example: Automated Backup Script
1#!/bin/bash 2 3BACKUP_DIR="/backups" 4SOURCE_DIR="/var/www/html" 5TIMESTAMP=$(date +%Y%m%d_%H%M%S) 6BACKUP_FILE="website_backup_$TIMESTAMP.tar.gz" 7 8# Create backup directory if it doesn't exist 9mkdir -p "$BACKUP_DIR" 10 11# Create compressed backup 12tar -czf "$BACKUP_DIR/$BACKUP_FILE" "$SOURCE_DIR" 13 14# Keep only the last 7 backups 15ls -t "$BACKUP_DIR"/website_backup_*.tar.gz | tail -n +8 | xargs rm -f 16 17echo "Backup completed: $BACKUP_FILE"
2. Efficiency
Tasks that take hours manually can be reduced to seconds. Whether it's processing thousands of log files, renaming files in bulk, or extracting data from multiple sources, a well-written script handles it instantly.
Example: Bulk File Renamer
1#!/bin/bash 2 3# Rename all .txt files to .bak in the current directory 4for file in *.txt; do 5 if [ -f "$file" ]; then 6 mv "$file" "${file%.txt}.bak" 7 echo "Renamed: $file -> ${file%.txt}.bak" 8 fi 9done
3. DevOps & CI/CD
Modern DevOps relies heavily on shell scripts for deployment pipelines, container orchestration, and infrastructure provisioning. Tools like Jenkins, GitHub Actions, and GitLab CI all use shell commands under the hood.
Example: Simple Deployment Script
1#!/bin/bash 2 3set -e # Exit immediately if a command fails 4 5APP_DIR="/opt/myapp" 6GIT_REPO="https://github.com/user/myapp.git" 7 8echo "Starting deployment..." 9 10cd "$APP_DIR" 11git pull origin main 12npm install 13npm run build 14pm2 restart myapp 15 16echo "Deployment completed successfully!"
4. System Administration
From user management to service control and security audits, shell scripting is the daily tool of every Linux system administrator.
Example: System Health Check
1#!/bin/bash 2 3echo "=== System Health Report ===" 4echo "Date: $(date)" 5echo "" 6echo "Uptime: $(uptime -p)" 7echo "Disk Usage:" 8df -h | grep -E "^/dev" 9echo "" 10echo "Memory Usage:" 11free -h 12echo "" 13echo "Top 5 CPU-consuming processes:" 14ps aux --sort=-%cpu | head -n 6
5. Portability
Shell scripts written for Bash run on Linux servers, macOS development machines, and Windows Subsystem for Linux (WSL). This cross-platform compatibility makes them incredibly versatile.
Popular Shells Explained
Not all shells are the same. While they share common syntax, each has unique features and use cases.
| Shell | Description | Path | Best For |
|---|---|---|---|
| Bash | Bourne Again SHell — the default on most Linux distributions | /bin/bash | General scripting, compatibility, learning |
| Sh | Original Bourne Shell — the POSIX standard | /bin/sh | Portable scripts, minimal environments |
| Zsh | Z Shell — default on macOS since Catalina | /bin/zsh | Interactive use, customization, modern features |
| Fish | Friendly Interactive Shell | /usr/bin/fish | Beginner-friendly interactive shell |
Which Shell Should You Use?
- Start with Bash: It has the largest community, most tutorials, and runs everywhere.
- Use Sh for portability: If your script needs to run on any POSIX-compliant system, write it in pure
sh. - Try Zsh for daily use: If you're on macOS, Zsh offers excellent autocompletion and plugin ecosystems like Oh My Zsh.
- Experiment with Fish: Its syntax is more intuitive for beginners, though less compatible with traditional shell scripts.
Core Concepts Every Beginner Should Know
Variables and Data Types
Shell variables are untyped — they store strings by default.
1#!/bin/bash 2 3# Variable assignment (no spaces around =) 4NAME="Tech3Space" 5VERSION=2 6IS_ACTIVE=true 7 8# Access variables with $ 9echo "Welcome to $NAME" 10echo "Version: $VERSION" 11 12# Command substitution 13CURRENT_DIR=$(pwd) 14FILES_COUNT=$(ls | wc -l) 15 16echo "Current directory: $CURRENT_DIR" 17echo "Files in directory: $FILES_COUNT"
User Input
1#!/bin/bash 2 3read -p "Enter your name: " USER_NAME 4read -sp "Enter password: " PASSWORD 5echo "" 6 7echo "Hello, $USER_NAME!"
Conditional Statements
1#!/bin/bash 2 3FILE="data.txt" 4 5if [ -f "$FILE" ]; then 6 echo "$FILE exists and is a regular file." 7elif [ -d "$FILE" ]; then 8 echo "$FILE is a directory." 9else 10 echo "$FILE does not exist." 11fi 12 13# Numeric comparison 14AGE=25 15if [ "$AGE" -ge 18 ]; then 16 echo "You are an adult." 17fi
Common Test Operators:
| Operator | Meaning |
|---|---|
-eq | Equal (numeric) |
-ne | Not equal (numeric) |
-gt | Greater than |
-lt | Less than |
-f | File exists and is regular |
-d | Directory exists |
-z | String is empty |
-n | String is not empty |
Loops
1#!/bin/bash 2 3# For loop with list 4for color in red green blue; do 5 echo "Color: $color" 6done 7 8# For loop with range 9for i in {1..5}; do 10 echo "Iteration $i" 11done 12 13# For loop with files 14for file in *.log; do 15 echo "Processing: $file" 16 gzip "$file" 17done 18 19# While loop 20COUNTER=1 21while [ $COUNTER -le 5 ]; do 22 echo "Counter: $COUNTER" 23 ((COUNTER++)) 24done
Functions
1#!/bin/bash 2 3# Define a function 4greet_user() { 5 local name=$1 # First argument 6 local greeting=$2 # Second argument 7 8 echo "$greeting, $name!" 9} 10 11# Call the function 12greet_user "Alice" "Good morning" 13greet_user "Bob" "Hello" 14 15# Function with return value 16get_disk_usage() { 17 df -h / | awk 'NR==2 {print $5}' 18} 19 20USAGE=$(get_disk_usage) 21echo "Root disk usage: $USAGE"
Error Handling
1#!/bin/bash 2 3set -euo pipefail 4# -e: Exit on error 5# -u: Treat unset variables as errors 6# -o pipefail: Catch errors in pipelines 7 8LOG_FILE="/var/log/myapp.log" 9 10# Check if log file exists before reading 11if [ ! -f "$LOG_FILE" ]; then 12 echo "Error: Log file not found at $LOG_FILE" >&2 13 exit 1 14fi 15 16echo "Log file found. Processing..."
Real-World Script Examples
Log Rotation Script
1#!/bin/bash 2 3LOG_DIR="/var/log/myapp" 4MAX_SIZE=10485760 # 10MB in bytes 5ARCHIVE_DIR="$LOG_DIR/archive" 6 7mkdir -p "$ARCHIVE_DIR" 8 9for logfile in "$LOG_DIR"/*.log; do 10 if [ -f "$logfile" ]; then 11 FILE_SIZE=$(stat -f%z "$logfile" 2>/dev/null || stat -c%s "$logfile") 12 13 if [ "$FILE_SIZE" -gt "$MAX_SIZE" ]; then 14 BASENAME=$(basename "$logfile") 15 TIMESTAMP=$(date +%Y%m%d_%H%M%S) 16 17 gzip -c "$logfile" > "$ARCHIVE_DIR/${BASENAME%.log}_$TIMESTAMP.gz" 18 > "$logfile" # Truncate the original file 19 20 echo "Rotated: $BASENAME ($(numfmt --to=iec $FILE_SIZE))" 21 fi 22 fi 23done 24 25# Remove archives older than 30 days 26find "$ARCHIVE_DIR" -name "*.gz" -mtime +30 -delete
Environment Setup Script
1#!/bin/bash 2 3# setup-dev-env.sh 4# One-command setup for new development machines 5 6set -e 7 8echo "🚀 Setting up development environment..." 9 10# Update package list 11sudo apt-get update 12 13# Install essential packages 14PACKAGES="git curl wget vim build-essential nodejs npm docker.io" 15sudo apt-get install -y $PACKAGES 16 17# Configure Git (customize these) 18git config --global user.name "Your Name" 19git config --global user.email "your.email@example.com" 20 21# Add user to docker group 22sudo usermod -aG docker $USER 23 24# Create project directories 25mkdir -p ~/projects ~/tools ~/backups 26 27echo "✅ Setup complete! Please log out and back in for Docker permissions."
Best Practices for Writing Shell Scripts
1. Always Use a Shebang
Start every script with #!/bin/bash or #!/bin/sh so the system knows which interpreter to use.
2. Quote Your Variables
Always wrap variables in double quotes to prevent word splitting and globbing issues:
1# Bad 2rm $filename 3 4# Good 5rm "$filename"
3. Use Meaningful Names
Name your variables and functions descriptively:
1# Bad 2x=$(date +%Y) 3 4# Good 5CURRENT_YEAR=$(date +%Y)
4. Add Comments
Explain the "why," not just the "what":
1# Rotate logs weekly to prevent disk space issues 2# See: https://company-wiki.com/log-management
5. Validate Inputs
Never trust user input or external data:
1INPUT_DIR=$1 2 3if [ -z "$INPUT_DIR" ]; then 4 echo "Error: Please provide a directory path." >&2 5 exit 1 6fi 7 8if [ ! -d "$INPUT_DIR" ]; then 9 echo "Error: $INPUT_DIR is not a valid directory." >&2 10 exit 1 11fi
6. Use set -euo pipefail
This combination catches most common scripting errors early:
1#!/bin/bash 2set -euo pipefail
7. Log Output
Redirect output to log files for debugging:
1exec > >(tee -a /var/log/myscript.log) 2exec 2> >(tee -a /var/log/myscript.log >&2)
Conclusion
Shell scripting is a fundamental skill that pays dividends throughout your tech career. Whether you're automating backups, deploying applications, or managing servers, the ability to write clean, reliable shell scripts will make you significantly more productive.
Start small: Write scripts for tasks you do repeatedly. Practice consistently: The more you script, the more patterns you'll recognize. Read others' code: Study scripts in open-source projects to learn idiomatic patterns.
The command line is your canvas — start scripting today.
Quick Reference Card
| Task | Command/Pattern |
|---|---|
| Make script executable | chmod +x script.sh |
| Run script | ./script.sh or bash script.sh |
| Print to stdout | echo "message" |
| Read user input | read -p "Prompt: " VAR |
| Check if file exists | [ -f "file" ] |
| Check if directory exists | [ -d "dir" ] |
| Loop through files | for f in *.txt; do ...; done |
| Get command output | VAR=$(command) |
| Exit with error | exit 1 |
| Suppress errors | command 2>/dev/null |
Happy scripting! If you found this guide helpful, share it with your team and bookmark it for future reference.