From 81c2a81c7d83b331df6ec7cf30ad9d55d0bc7d0b Mon Sep 17 00:00:00 2001 From: Tero Date: Fri, 28 Aug 2026 17:55:20 +0300 Subject: [PATCH] Add exclude option to find_duplicate_pictures.sh --- README.md | 13 ++++++++++ find_duplicate_pictures.sh | 49 ++++++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ad4c308..7c90a1e 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,9 @@ three independent checks: # Scan specific roots ./find_duplicate_pictures.sh /home/user/Pictures /mnt/backup +# Scan a collection while skipping cache and export directories +./find_duplicate_pictures.sh --exclude ~/Pictures/Cache --exclude ~/Pictures/Exports ~/Pictures + # Scan the whole filesystem (default when no arguments given) ./find_duplicate_pictures.sh @@ -50,6 +53,16 @@ chmod +x find_duplicate_pictures.sh | `FOLLOW_LINKS` | `0` (off) | Set to `1` to follow symlinks while scanning | | `REPORT` | *(none)* | Path to a file where the full report is also saved | +### Directory exclusions + +Use `-x DIR` or `--exclude DIR` to skip a directory and all of its contents. +The option may be repeated and accepts paths containing spaces. Relative paths +are supported for both scan roots and exclusions. + +```bash +./find_duplicate_pictures.sh -x ~/Pictures/Cache -x ~/Pictures/Exports ~/Pictures +``` + ### Examples ```bash diff --git a/find_duplicate_pictures.sh b/find_duplicate_pictures.sh index a1d08e9..8d4b4b2 100644 --- a/find_duplicate_pictures.sh +++ b/find_duplicate_pictures.sh @@ -14,18 +14,22 @@ # pre-filter before hashing. # # Usage: -# ./find_duplicate_pictures.sh [DIR ...] +# ./find_duplicate_pictures.sh [--exclude DIR ...] [DIR ...] # ./find_duplicate_pictures.sh # defaults to "/" (whole system) # # Examples: # ./find_duplicate_pictures.sh / /mnt/c /mnt/d # ./find_duplicate_pictures.sh ~/Pictures +# ./find_duplicate_pictures.sh --exclude ~/Pictures/Cache ~/Pictures # # Options (environment variables): # HASH_CMD=sha256sum # override hash tool (sha256sum, shasum -a 256, md5sum) # FOLLOW_LINKS=1 # follow symlinks while scanning (default: off) # REPORT=report.txt # also write the report to a file # +# Options: +# -x, --exclude DIR # skip DIR and everything below it (repeatable) +# set -u @@ -82,12 +86,46 @@ build_ext_expr() { # Arguments / scan roots # --------------------------------------------------------------------------- -if [ "$#" -gt 0 ]; then - ROOTS=("$@") -else +ROOTS=() +EXCLUDE_DIRS=() +while [ "$#" -gt 0 ]; do + case "$1" in + -x|--exclude) + if [ "$#" -lt 2 ]; then + echo "ERROR: $1 requires a directory path." >&2 + exit 1 + fi + EXCLUDE_DIRS+=("$2") + shift 2 + ;; + --) + shift + ROOTS+=("$@") + break + ;; + *) + ROOTS+=("$1") + shift + ;; + esac +done + +if [ "${#ROOTS[@]}" -eq 0 ]; then ROOTS=("/") fi +# Use absolute paths because find emits paths relative to the supplied root. +for index in "${!ROOTS[@]}"; do + if [ -d "${ROOTS[$index]}" ]; then + ROOTS[$index]=$(cd "${ROOTS[$index]}" && pwd -P) + fi +done +for index in "${!EXCLUDE_DIRS[@]}"; do + if [ -d "${EXCLUDE_DIRS[$index]}" ]; then + EXCLUDE_DIRS[$index]=$(cd "${EXCLUDE_DIRS[$index]}" && pwd -P) + fi +done + FIND_LINK_FLAG=() if [ "${FOLLOW_LINKS:-0}" = "1" ]; then FIND_LINK_FLAG=(-L) @@ -98,6 +136,9 @@ PRUNE_EXPR=() for d in $PRUNE_DIRS; do PRUNE_EXPR+=( -path "$d" -prune -o ) done +for d in "${EXCLUDE_DIRS[@]}"; do + PRUNE_EXPR+=( -path "$d" -prune -o ) +done # Extension expression mapfile -t EXT_EXPR < <(build_ext_expr)