From: Steven Baltakatei Sandoval Date: Sat, 1 Aug 2026 19:04:46 +0000 (+0000) Subject: feat(user/gthumb/prune.org):Prune files via gThumb X-Git-Url: https://zdv2.bktei.com/gitweb/BK-2020-03.git/commitdiff_plain/f59f83dae938c1b7832f8b01b731fc753990bea1?ds=sidebyside feat(user/gthumb/prune.org):Prune files via gThumb --- diff --git a/user/gthumb/prune.org b/user/gthumb/prune.org new file mode 100644 index 0000000..d9d211e --- /dev/null +++ b/user/gthumb/prune.org @@ -0,0 +1,257 @@ +#+AUTHOR: Steven Baltakatei Sandoval +#+DATE: 2026-08-01 +#+TITLE: gThumb prune + +** Summary +This work describes a way to quickly move groups of photograph files +(e.g. ~DSC00001.JPG~, ~DSC00001.ARW~, etc.) as well as associated +sidecar files into desired directories via keyboard shortcuts in +gThumb. + +The method centers around a Bash script named ~gthumb-prune.sh~ which +may be bound to a gThumb keyboard shortcut to quickly move selected +image files to a desired location. The Bash script is run by a short +shell script specified in gThumbʼs 🔧 → Personalize… menu. +Additionally, sidecar files as well as associated files sharing the +same file name stem (i.e. ~../*/DSC00001.*~) are also grouped and +moved to the desired location. + +** Stats +Version: 0.0.4 +License: GPLv3+ +gThumb version: ~3.12.6~ + +** Usage +Let us say, that we have a set of photos in the following +locations: + +#+begin_example +. +├── ARW +│   ├── DSC00001.ARW +│   ├── DSC00001.ARW.ots +│   ├── DSC00002.ARW +│   ├── DSC00002.ARW.ots +│   ├── DSC00003.ARW +│   └── DSC00003.ARW.ots +└── JPG + ├── DSC00001.JPG + ├── DSC00001.JPG.ots + ├── DSC00002.JPG + ├── DSC00002.JPG.ots + ├── DSC00002.JPG + └── DSC00002.JPG.ots +#+end_example + +Let us say gThumb has the ~JPG~ directory open and we have selected +~DSC00002.JPG~ which we want to keep. We also want to keep the +associated ~DSC00002.JPG.ots~ as well as the ~DSC00002.ARW~ and +~DSC00002.ARW.ots~ files in the neighboring ~ARW~ directory. We wish +to discard all other photograph files. + +First, we configure a ~keep~ shortcut. In the gThumbʼs 🔧 → +Personalize… menu, click the *New* button. Set the *Name* to +~keep~. Set the *Command* to: + +: fscript="$HOME/.local/bin/gthumb-prune.sh"; if [ -f "$fscript" ]; then /bin/bash "$fscript" "keep" %F; fi; + +Where ~$HOME/.local/bin/gthumb-prune.sh~ is the path for the Bash +script (see the ~Code~ section below). + +Set the *Shortcut* to ~Shift+K~. Make sure the box “Terminal command +(shell script)” box is checked and the “Execute command once for every +file” is *not* checked. Click the *Save* button to close the ~Edit +Command~ window. Click the *Ok* button to close the ~Commands~ window. + +Now, with ~DSC00002.JPG~ selected, press ~Shift+K~ on your +keyboard. The image should disappear from view and a new directory +should appear in the the left navbar, reflecting a new directory structure. + +#+begin_example +. +├── ARW +│   ├── DSC00001.ARW +│   ├── DSC00001.ARW.ots +│   ├── DSC00003.ARW +│   └── DSC00003.ARW.ots +├── JPG +│   ├── DSC00001.JPG +│   ├── DSC00001.JPG.ots +│   ├── DSC00003.JPG +│   └── DSC00003.JPG.ots +└── keep + ├── ARW + │   ├── DSC00002.ARW + │   └── DSC00002.ARW.ots + └── JPG + ├── DSC00002.JPG + └── DSC00002.JPG.ots +#+end_example + +The ~DSC0002.*~ files (4 in total) have been moved to now reside +within a ~keep~ directory with their ~JPG~ and ~ARW~ subdirectory +structure preserved. Thus, the ~./JPG/~ and ~./ARW/~ directories have +been pruned. + +If we want to trash the remaining ~DSC00001.*~ and ~DSC00003.*~ files, +we can create another shortcut to group and move files into a ~.trash~ +directory next to ~keep~. + +Repeat the procedure for creating a shortcut, but *Name* it ~rmall~ +and set the *Command* to be: + +: fscript="$HOME/.local/bin/gthumb-prune.sh"; if [ -f "$fscript" ]; then /bin/bash "$fscript" ".trash" %F; fi; + +Note the change from ~keep~ to ~.trash~. Set the keyboard *Shortcut* +to ~Shift+T~. + +Now, with gThumb looking at the contents of ~./JPG~, and with +~DSC00001.JPG~ and ~DSC00003.JPG~ selected, press ~Shift+T~. The two +images should disappear, leaving ~./JPG~ now empty. The navbar to the +left should not show any change since ~.trash~ is a dot directory and +so is normally hidden. Nonetheless, the directory structure has now +changed to: + +#+begin_example +. +├── ARW +├── JPG +├── keep +│   ├── ARW +│   │   ├── DSC00002.ARW +│   │   └── DSC00002.ARW.ots +│   └── JPG +│   ├── DSC00002.JPG +│   └── DSC00002.JPG.ots +└── .trash + ├── ARW + │   ├── DSC00001.ARW + │   ├── DSC00001.ARW.ots + │   ├── DSC00003.ARW + │   └── DSC00003.ARW.ots + └── JPG + ├── DSC00001.JPG + ├── DSC00001.JPG.ots + ├── DSC00003.JPG + └── DSC00003.JPG.ots +#+end_Example + +So, the contents of ~./JPG/~ may be pruned by selecting files you want +to keep and pressing ~Shift+K~ and by selecting files you want to +trash and pressing ~Shift+T~. Then, if you are really sure about +deleting the undesired files, delete the contents of ~.trash~ via the +terminal command: + +: $ rm -r ./.trash/* + +** Code +gthumb-prune.sh: + +#+begin_code +#!/bin/bash +# Desc: Prunes files selected by gThumb. +# Note: Moves files into a directory with a specified name. +# Usage: gthumb-prune.sh [STR dirname] [FILES…] +# Example: $HOME/.local/bin/gthumb-prune.sh keep %F +# If '%F' expanded to `~/Pics/MyJPGs/DSC00001.JPG`, then +# "$HOME/Pics/*/DSC00001.*" files are moved to: +# '~/Pics/keep/MyJPGs/' . +# +# If '%F' expanded to multiple files, then the move +# operations to 'keep' are performed for each file. +# Input: arg1 str directory to send selected files +# arg2+ str path of selected file(s) +# Version: 0.0.4 + +fdebug=/tmp/gtPruneLog.txt; + +yell() { echo "$0: $*" >&2; } # print script path and all args to stderr +die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status +must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails +main() { + # Validate inputs + local groupName re1 fina; + ## Interpret first argument as name of group directory to move files into. + groupName="$1"; # e.g. 'keep' + ### RegEx matches: + ### - Forward slash (i.e. path) anywhere. + ### - A string consisting of just '.' (current working dir) + ### - A string consisting of just '..' (parent dir of working dir) + re1='/|^\.$|^\.\.$'; + if [[ "$groupName" =~ $re1 ]]; then + die "FATAL:Group '${groupName}' must not be a path."; + fi; + if [[ -z "$groupName" ]]; then + die "FATAL:Group '${groupName}' may not be blank."; + fi; + shift; + + ## Interpret remaining arguments as files and populate input file array + fina=("$@"); + + ## Validate input file array + ### Handle no argument case. + if [[ $# -le 0 ]]; then + die "FATAL:No arguments:$#"; + fi; + ### Check each remaining array element is a file. + local fin; + for fin in "${fina[@]}"; do + if [[ ! -f "$fin" ]]; then + die "FATAL:Not a file:${fin}"; + fi; + done; + + + # Process each file + local f; + for f in "${fina[@]}"; do + # Set up variables + local fBase dParent dParParent fNoExt dGroup; + fBase="$(basename "$f")"; + dParent="$(dirname "$f")"; + dParParent="$(dirname "$dParent")"; + fNoExt="${fBase%.*}"; + dGroup="${dParParent}/${groupName}"; + yell "DEBUG:fdebug:${fdebug}"; + yell "DEBUG:f:${f}"; # e.g. ~/Pics/MyJPGs/DSC00001.JPG + yell "DEBUG:fBase:${fBase}"; # e.g. DSC00001.JPG + yell "DEBUG:fNoExt:${fNoExt}"; # e.g. DSC00001 + yell "DEBUG:dParent:${dParent}"; # e.g. ~/Pics/MyJPGs/ + yell "DEBUG:dParParent:${dParParent}"; # e.g. ~/Pics/ + yell "DEBUG:dGroup:${dGroup}"; # e.g. ~/Pics/keep/ + + # Move sibling files sharing the stem + local fSrc subDirName dDest; + for fSrc in "${dParParent}"/*/"${fNoExt}".*; do + if [ ! -e "$fSrc" ]; then continue; fi; + subDirName="$(basename "$(dirname "$fSrc")")"; + dDest="${dGroup}/${subDirName}"; + yell "DEBUG:fSrc:${fSrc}"; # e.g. ~/Pics/MyARWs/DSC00001.ARW.ots + yell "DEBUG:subDirName:${subDirName}"; # e.g. MyARWs + yell "DEBUG:dDest:${dDest}"; # e.g. ~/Pics/keep/MyARWs/ + if [ ! -d "$dDest" ]; then + yell "STATUS:Destination dir does not exist. Creating:${dDest}"; + must mkdir -p "$dDest"; + else + yell "STATUS:Destination dir exists:${dDest}"; + fi; + yell "STATUS: Moving ${fSrc} → ${dDest}/"; + + # Perform move ( e.g. mv ~/Pics/MyARWs/DSC00001.ARW.ots ~/Pics/keep/MyARWs/ ) + mv -n "$fSrc" "${dDest}/"; + + # Check if move failed. + if [[ -e "$fSrc" ]]; then + yell "ERROR:Failed to move ${fSrc} → ${dDest}"; + fi; + done; + done; +}; + +main "$@" 1>>"$fdebug" 2>&1; + +# Author: Steven Baltakatei Sandoval +# License: GPLv3+ +#+end_code +