I created this Bash script for Linux that organizes directory contents by file extension. The script generates subdirectories for each extension found and copies the files into new appropriate directories while preserving the originals. All copies go into a newly-created sorted_files directory.
I've found it very useful for having the contents separated out in this way. Music (.mp3, .ogg, and .wav) - Documents (.pdf, .docx, .odt) - Videos (.mkv, .mp4, .mov) and Images (.jpg, .png. webp), and while keeping the original directories completely untouched.
The script handles case-insensitivity (JPEG / jpeg) and files without extensions, which are common in the Linux OS. The script preserves all original file metadata and provides a summary report of the organized assets without modifying or deleting the source data.
#!/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)}"
# Check if 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
# Find all files (excluding hidden ones) and process them
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
# Get the filename and extension
filename=$(basename "$file")
# Extract extension
if [[ "$filename" == *.* ]]; then
extension="${filename##*.}"
extension=$(echo "$extension" | tr '[:upper:]' '[:lower:]') # Convert to lowercase
else
extension="no_extension"
fi
# Create directory for extension if it doesn't exist
mkdir -p "$SORTED_DIR/$extension"
# Copy file 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
# Display summary
echo ""
echo "Sorting complete! Processed $total_files files."
echo "Files organized by extension:"
# Print extension counts in alphabetical order
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."
I hope you find the script useful. The source code and a more thorough README can be found in the cli_tools repo on my GitHub site.
Ben Santora - January 2026
Top comments (0)