Skip to content

Instantly share code, notes, and snippets.

@ralph
Created February 17, 2026 15:26
Show Gist options
  • Select an option

  • Save ralph/6d1e535267a99cf1fe52b504e1f25853 to your computer and use it in GitHub Desktop.

Select an option

Save ralph/6d1e535267a99cf1fe52b504e1f25853 to your computer and use it in GitHub Desktop.

Revisions

  1. ralph created this gist Feb 17, 2026.
    128 changes: 128 additions & 0 deletions compare_folders.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,128 @@
    #!/bin/bash

    # Example file structure:
    # .
    # ├── jpg
    # │   ├── 123.jpg
    # │   ├── 789.jpg
    # │   └── 890.jpg
    # └── raw
    # ├── 123.raw
    # ├── 456.raw
    # └── 789.raw
    #
    # ❯ ./compare_folders.sh raw jpg
    # Only in raw:
    # raw/456.raw
    #
    # Only in jpg:
    # jpg/890.jpg
    #
    # ========================================
    # WARNING: Deletion is PERMANENT and
    # UNRECOVERABLE! Files will NOT go to Trash.
    # ========================================
    #
    # Would you like to delete the unmatched files?
    # 1) Delete files in raw that are missing in jpg
    # 2) Delete files in jpg that are missing in raw
    # 3) Do nothing and exit
    #
    # Choose [1/2/3]:
    #
    if [ $# -lt 2 ]; then
    echo "Usage: $0 <folder1> <folder2>"
    exit 1
    fi

    dir1="$1"
    dir2="$2"

    # Extract basenames without extensions
    names1=$(ls "$dir1" | sed 's/\.[^.]*$//' | sort)
    names2=$(ls "$dir2" | sed 's/\.[^.]*$//' | sort)

    only_in_1=$(comm -23 <(echo "$names1") <(echo "$names2"))
    only_in_2=$(comm -13 <(echo "$names1") <(echo "$names2"))

    if [ -n "$only_in_1" ]; then
    echo "Only in $dir1:"
    for name in $only_in_1; do
    ls "$dir1"/"$name".* 2>/dev/null
    done
    echo ""
    fi

    if [ -n "$only_in_2" ]; then
    echo "Only in $dir2:"
    for name in $only_in_2; do
    ls "$dir2"/"$name".* 2>/dev/null
    done
    echo ""
    fi

    if [ -z "$only_in_1" ] && [ -z "$only_in_2" ]; then
    echo "All file basenames match between the two folders."
    exit 0
    fi

    # Offer to delete
    echo "========================================"
    echo "WARNING: Deletion is PERMANENT and"
    echo "UNRECOVERABLE! Files will NOT go to Trash."
    echo "========================================"
    echo ""
    echo "Would you like to delete the unmatched files?"
    echo " 1) Delete files in $dir1 that are missing in $dir2"
    echo " 2) Delete files in $dir2 that are missing in $dir1"
    echo " 3) Do nothing and exit"
    echo ""
    read -rp "Choose [1/2/3]: " choice

    case "$choice" in
    1)
    if [ -z "$only_in_1" ]; then
    echo "No unmatched files in $dir1. Nothing to delete."
    exit 0
    fi
    echo ""
    echo "The following files will be PERMANENTLY deleted:"
    for name in $only_in_1; do
    ls "$dir1"/"$name".* 2>/dev/null
    done
    echo ""
    read -rp "Are you sure? Type YES to confirm: " confirm
    if [ "$confirm" = "YES" ]; then
    for name in $only_in_1; do
    rm -v "$dir1"/"$name".* 2>/dev/null
    done
    echo "Done."
    else
    echo "Aborted."
    fi
    ;;
    2)
    if [ -z "$only_in_2" ]; then
    echo "No unmatched files in $dir2. Nothing to delete."
    exit 0
    fi
    echo ""
    echo "The following files will be PERMANENTLY deleted:"
    for name in $only_in_2; do
    ls "$dir2"/"$name".* 2>/dev/null
    done
    echo ""
    read -rp "Are you sure? Type YES to confirm: " confirm
    if [ "$confirm" = "YES" ]; then
    for name in $only_in_2; do
    rm -v "$dir2"/"$name".* 2>/dev/null
    done
    echo "Done."
    else
    echo "Aborted."
    fi
    ;;
    *)
    echo "No files deleted."
    ;;
    esac