Sort by File Extension - Bash Script

Published: (January 1, 2026 at 12:48 PM EST)
2 min read
Source: Dev.to

Source: Dev.to

Overview

This Bash script organizes the contents of a directory by file extension. It creates a sorted_files folder inside the source directory and, for each distinct extension found, generates a subdirectory containing copies of the matching files.

Key features

  • Works recursively on all files (excluding hidden ones).
  • Handles case‑insensitive extensions (e.g., JPEG and jpeg).
  • Places files without an extension into a no_extension folder.
  • Preserves original file timestamps and metadata.
  • Leaves the source files untouched; only copies are created.
  • Provides a progress indicator and a summary report of how many files were processed per extension.

Typical use cases include sorting music collections (.mp3, .ogg, .wav), documents (.pdf, .docx), videos (.mkv, .mp4), and images (.jpg, .png).

Usage

./sort_by_extension.sh [source_directory]
  • If source_directory is omitted, the script defaults to the current working directory.
  • The resulting structure will be created at /sorted_files.

Script

#!/bin/bash

# Recursive File Extension Sorter
# Organizes files by their extensions into a sorted_files directory

# Default to current directory if no argument is provided
SOURCE_DIR="${1:-$(pwd)}"

# Verify that the source directory exists
if [ ! -d "$SOURCE_DIR" ]; then
    echo "Error: Directory '$SOURCE_DIR' does not exist."
    exit 1
fi

# Create the sorted_files directory
SORTED_DIR="$SOURCE_DIR/sorted_files"
mkdir -p "$SORTED_DIR"

# Initialize counters
declare -A extension_counts
total_files=0

# Scan and process files
echo "Scanning and organizing files in '$SOURCE_DIR'..."
echo "This may take a while for large directories."
echo ""

# Find all non‑hidden files
find "$SOURCE_DIR" -type f ! -path '*/\.*' | while read -r file; do
    # Skip files already in the sorted directory
    if [[ "$file" == "$SORTED_DIR"* ]]; then
        continue
    fi

    # Extract filename and extension
    filename=$(basename "$file")
    if [[ "$filename" == *.* ]]; then
        extension="${filename##*.}"
        extension=$(echo "$extension" | tr '[:upper:]' '[:lower:]')  # normalize to lowercase
    else
        extension="no_extension"
    fi

    # Ensure the extension directory exists
    mkdir -p "$SORTED_DIR/$extension"

    # Copy file while preserving timestamps
    cp -p "$file" "$SORTED_DIR/$extension/"

    # Update counters
    ((total_files++))
    ((extension_counts[$extension]++))

    # Show progress every 100 files
    if (( total_files % 100 == 0 )); then
        echo "Processed $total_files files..."
    fi
done

# Summary report
echo ""
echo "Sorting complete! Processed $total_files files."
echo "Files organized by extension:"

# List counts alphabetically
for ext in $(printf '%s\n' "${!extension_counts[@]}" | sort); do
    echo "  $ext: ${extension_counts[$ext]} files"
done

echo ""
echo "All files have been copied to: $SORTED_DIR"
echo "Note: Original files remain unchanged."
Back to Blog

Related posts

Read more »

Why Bash Syntax Feels So Arcane

Bash’s Historical Roots Bash runs deep beneath the surface of Linux. With only a line or two, it can take full control of your machine. It is often viewed by b...