LearnAI ToolsCareerPractice BuildsPlayDevelopmentContact
Linux & Command LineBeginner~1 hour

Downloads Folder Organizer

Write a script that sorts a messy folder into Images, Documents, Videos, Music, Archives and Other — with a safe dry-run mode and support for awkward file names.

Bash Scriptscase StatementsLoopsSafe File Handling

Overview

Everyone’s Downloads folder becomes a mess. This project automates the clean-up: a script that moves each file into a folder based on its type. Along the way you will practise the habits that separate safe scripts from dangerous ones — a dry-run mode, quoting, never overwriting, and skipping anything unexpected.

Prerequisites

  • Managing files with mv and mkdir — lesson 4.
  • Scripts, loops, functions and quoting — lesson 12.

Step 1: A Messy Folder

Create a practice folder rather than using your real Downloads at first: mkdir -p ~/Downloads/old-project, then create a mix of files with touch — including an upper-case extension, a name with a space and a file with no extension:

ls Downloads
Output

Click Run to see what this code prints.

Notice that ls puts quotes around my report.docx to show the space is part of the name — exactly the kind of file that breaks careless scripts.

Step 2: The Script

~/bin/organize.sh
#!/bin/bash
# organize.sh: sort files into folders by type
# Usage: organize.sh <folder> [--dry-run]
set -euo pipefail
dir="${1:?Usage: $0 <folder> [--dry-run]}"
dry_run=false
[ "${2:-}" = "--dry-run" ] && dry_run=true
folder_for() {
case "${1,,}" in
jpg|jpeg|png|gif|webp|svg) echo "Images" ;;
pdf|doc|docx|txt|md|odt) echo "Documents" ;;
mp4|mov|mkv|avi) echo "Videos" ;;
mp3|wav|flac) echo "Music" ;;
zip|tar|gz|rar|7z) echo "Archives" ;;
*) echo "Other" ;;
esac
}
moved=0
for file in "$dir"/*; do
[ -f "$file" ] || continue # skip folders
name="$(basename "$file")"
ext="${name##*.}"
[ "$ext" = "$name" ] && ext="" # no extension
target="$dir/$(folder_for "$ext")"
if $dry_run; then
echo "would move $name -> $(basename "$target")/"
else
mkdir -p "$target"
mv -n -- "$file" "$target/"
echo "moved $name -> $(basename "$target")/"
fi
moved=$((moved + 1))
done
echo "$moved file(s) $($dry_run && echo "would be" || echo "were") organised."
  • case matches the extension against patterns; ${1,,} lower-cases it, so JPG and jpg are treated the same.
  • ${name##*.} takes everything after the last dot; if that is the whole name, the file has no extension.
  • [ -f "$file" ] || continue skips folders such as old-project.
  • mv -n never overwrites an existing file, and -- stops a name starting with a dash being read as an option.
  • Every variable is quoted, so names with spaces work.

Step 3: Dry Run First

Before moving anything, see what the script would do:

chmod +x ~/bin/organize.sh
~/bin/organize.sh ~/Downloads --dry-run
Output

Click Run to see what this code prints.

Step 4: Organise for Real

~/bin/organize.sh ~/Downloads
Output

Click Run to see what this code prints.

tree ~/Downloads
Output

Click Run to see what this code prints.

The folder old-project was left alone. Running the script again changes nothing, because there are no loose files left — a property called idempotence that every good automation script should have:

~/bin/organize.sh ~/Downloads
Output

Click Run to see what this code prints.

Extend This Project

  • Add more categories, such as Code for .py, .js and .html files.
  • Sort Images further into year folders using the file’s modification date (date -r file +%Y).
  • Run it automatically every evening with cron.
  • Write a log of every move, so you can find where a file went.

Summary

You built a useful everyday tool and practised the safety habits that matter in every script that moves or deletes files: dry runs, quoting, no overwriting and idempotence.