Skip to content

Enhancements — File Organizer

Extend the core organizer once the basics work.

Enhancement 1 — Undo / Rollback

Write a manifest so you can reverse every move:

MANIFEST="${SCRIPT_DIR}/organizer_manifest.csv"

move_file() {
    local src="$1" dest_dir="$2"
    local filename dest
    filename="$(basename "$src")"
    dest="$(unique_dest "${dest_dir}/${filename}")"

    [[ "$DRY_RUN" == true ]] && { log_info "[DRY RUN] $src$dest"; return; }

    mkdir -p "$dest_dir"
    mv -- "$src" "$dest"
    echo "$(date +%s),${src},${dest}" >> "$MANIFEST"
    log_info "moved: $src$dest"
}

Add an --undo flag that reads the manifest and reverses every move:

undo_moves() {
    [[ -f "$MANIFEST" ]] || { echo "No manifest found at $MANIFEST"; exit 1; }
    tac "$MANIFEST" | while IFS=, read -r ts src dest; do
        if [[ -f "$dest" ]]; then
            mkdir -p "$(dirname "$src")"
            mv -- "$dest" "$src"
            echo "restored: $dest$src"
        else
            echo "WARN: $dest not found, skipping"
        fi
    done
    echo "Undo complete"
}

Enhancement 2 — Custom Rules via Config

Let users define their own category mappings in a config file:

# ~/.config/file_organizer.conf
# FORMAT: category:ext1,ext2,ext3

Work:doc,docx,xlsx,pptx,pdf
Personal:jpg,png,mp4,mp3
Projects:sh,py,go,rs,js

Load custom rules:

load_custom_rules() {
    local conf="${HOME}/.config/file_organizer.conf"
    [[ -f "$conf" ]] || return 0
    while IFS=: read -r category exts; do
        [[ "$category" =~ ^# ]] && continue   # skip comments
        IFS=, read -r -a ext_list <<< "$exts"
        for ext in "${ext_list[@]}"; do
            CUSTOM_RULES["${ext,,}"]="$category"
        done
    done < "$conf"
}

declare -A CUSTOM_RULES

get_category() {
    local ext="${1,,}"
    # Check custom rules first
    [[ -n "${CUSTOM_RULES[$ext]:-}" ]] && echo "${CUSTOM_RULES[$ext]}" && return
    # Fall back to built-in mapping
    case "$ext" in
        jpg|jpeg|png|gif|bmp|svg|webp) echo "Images"    ;;
        # ... rest of built-in rules
        *) echo "Other" ;;
    esac
}

Enhancement 3 — Recursive Mode

Organize nested directories, not just the top level:

# Add flag: -r, --recursive
RECURSIVE=false

# Change find command in main():
if [[ "$RECURSIVE" == true ]]; then
    find_opts=()
else
    find_opts=(-maxdepth 1)
fi

while IFS= read -r -d '' file; do
    ...
done < <(find "$target" "${find_opts[@]}" -type f -print0)

Recursive mode moves files out of nested directories

If you have Downloads/Work/report.pdf, recursive mode will move it to Downloads/Documents/report.pdf. The Work/ subdirectory won't be deleted automatically. Add empty-directory cleanup if you want it fully tidy.

Enhancement 4 — Watch Mode (inotify)

Continuously organize a directory as new files arrive:

# Requires: inotifywait (sudo apt install inotify-tools)

watch_dir() {
    local target="$1"
    echo "Watching $target for new files..."
    inotifywait -m -e close_write,moved_to "$target" --format '%f' |
    while read -r filename; do
        local filepath="${target}/${filename}"
        [[ -f "$filepath" ]] || continue
        local ext="${filename##*.}"
        local category
        category="$(get_category "$ext")"
        move_file "$filepath" "${target}/${category}"
        log_info "watch: auto-moved $filename$category/"
    done
}

Enhancement 5 — Date-Based Sorting

For image-heavy workflows, sort by creation date instead of extension:

get_date_category() {
    local file="$1"
    # Try EXIF date first (requires exiftool)
    local date_str
    date_str=$(exiftool -DateTimeOriginal -d "%Y/%m" "$file" 2>/dev/null | awk -F': ' '{print $2}' | tr -d ' ')
    if [[ -n "$date_str" ]]; then
        echo "$date_str"
        return
    fi
    # Fall back to file modification time
    date -d "@$(stat -c %Y "$file")" '+%Y/%m'
}

This creates a hierarchy like Images/2024/01/photo.jpg instead of Images/photo.jpg.

Enhancement 6 — Statistics Report

After organizing, print a summary table:

print_stats() {
    echo ""
    echo "=== Organizer Summary ==="
    printf "%-15s %s\n" "Category" "Files moved"
    printf "%-15s %s\n" "--------" "-----------"
    for dir in "${TARGET_DIR}"/*/; do
        local category count
        category="$(basename "$dir")"
        count=$(find "$dir" -maxdepth 1 -type f | wc -l)
        (( count > 0 )) && printf "%-15s %d\n" "$category" "$count"
    done
}

testing | solution