Created
July 14, 2025 15:36
-
-
Save guyjin/a0137aeb47f2ce0cb7341160222b72a3 to your computer and use it in GitHub Desktop.
Batch create thumbnails with ImageMagick in zsh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/zsh | |
| # ^ Changed shebang to /bin/zsh | |
| # Output directory for thumbnails | |
| OUTPUT_DIR=".thumbs" | |
| FORCE=0 | |
| DRY_RUN=0 | |
| THUMBNAIL_DIMENSIONS="200x150" # Default size for landscape/portrait images | |
| # ANSI colors | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| CYAN='\033[1;36m' | |
| RED='\033[0;31m' | |
| NC='\033[0m' # No color | |
| # --- Argument Parsing --- | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| --force) | |
| FORCE=1 | |
| shift | |
| ;; | |
| --dry-run) | |
| DRY_RUN=1 | |
| shift | |
| ;; | |
| *) | |
| echo -e "${RED}Unknown option: $1${NC}" >&2 | |
| echo "Usage: $(basename "$0") [--force] [--dry-run]" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| # --- Helper Function for Image Processing (THIS FUNCTION WILL BE RE-DEFINED FOR XARGS) --- | |
| # We keep this for clarity/testing in a non-xargs context, but it won't be directly called by xargs. | |
| # The version used by xargs is injected into the 'bash -c' string. | |
| # (Leaving this 'local' definition commented out as it's not strictly necessary for the xargs path) | |
| # process_image() { ... } | |
| # Export variables needed by process_image | |
| export OUTPUT_DIR FORCE DRY_RUN THUMBNAIL_DIMENSIONS | |
| export GREEN YELLOW CYAN RED NC | |
| # --- Main Logic --- | |
| # Collect image list first using Zsh's array assignment with null splitting | |
| echo -e "${CYAN}Searching for images...${NC}" | |
| local -a image_list # Declare as array | |
| image_list=( "${(@0)$(find . -type f \( -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' \) -print0)}" ) | |
| # Check if any images were found | |
| if (( ${#image_list[@]} == 0 )); then | |
| echo -e "${RED}No images found.${NC}" | |
| exit 0 | |
| fi | |
| # Track if any thumbnails were actually created by ImageMagick (not just dry-run/skipped) | |
| local actual_thumbs_created=0 | |
| echo -e "${CYAN}Processing images...${NC}" | |
| # Define the function as a string to be passed to bash -c | |
| # We need to escape any single quotes within the function if the outer string uses single quotes. | |
| # Here-doc (<<<) makes this easier. | |
| read -r -d '' PROCESS_IMAGE_FUNC_DEF << 'EOF' | |
| process_image() { | |
| local img="$1" | |
| local thumb_full_path="" | |
| local rel_path="${img#./}" | |
| thumb_full_path="$OUTPUT_DIR/$rel_path" | |
| local thumb_dir=$(dirname "$thumb_full_path") | |
| if [[ $FORCE -eq 0 && -f "$thumb_full_path" ]]; then | |
| echo -e "${YELLOW}Skipping (exists): $thumb_full_path${NC}" | |
| echo "SKIPPED" | |
| return 0 | |
| fi | |
| if [[ $DRY_RUN -eq 1 ]]; then | |
| if [[ $FORCE -eq 1 && -f "$thumb_full_path" ]]; then | |
| echo -e "${CYAN}Would recreate: $thumb_full_path${NC}" | |
| else | |
| echo -e "${CYAN}Would create: $thumb_full_path${NC}" | |
| fi | |
| echo "DRY_RUN" | |
| return 0 | |
| fi | |
| if ! mkdir -p "$thumb_dir"; then | |
| echo -e "${RED}Error: Failed to create directory: $thumb_dir${NC}" >&2 | |
| echo "ERROR" | |
| return 1 | |
| fi | |
| local dimensions=$(magick identify -format "%w %h" "$img" 2>/dev/null) | |
| if [ -z "$dimensions" ]; then | |
| echo -e "${RED}Error: Unable to read image: $img${NC}" >&2 | |
| echo "ERROR" | |
| return 1 | |
| fi | |
| # Use bash array indexing (0-based) inside the bash -c subshell | |
| # We are in a bash subshell, so `read -A` (zsh) won't work, stick to bash's `read -a` | |
| local -a dims_array | |
| read -r -a dims_array <<< "$dimensions" | |
| local width="${dims_array[0]}" # Bash arrays are 0-indexed | |
| local height="${dims_array[1]}" | |
| local size="$THUMBNAIL_DIMENSIONS" | |
| if (( width > height )); then | |
| size="200x150" | |
| else | |
| size="150x200" | |
| fi | |
| if magick "$img" -thumbnail "$size^" -gravity center -extent "$size" "$thumb_full_path"; then | |
| echo -e "${GREEN}Created: $thumb_full_path${NC}" | |
| echo "CREATED" | |
| return 0 | |
| else | |
| echo -e "${RED}Error: Failed to convert image: $img${NC}" >&2 | |
| echo "ERROR" | |
| return 1 | |
| fi | |
| } | |
| EOF | |
| # The xargs command now passes the function definition and then calls it | |
| results=$(printf '%s\0' "${image_list[@]}" | xargs -0 -n 1 -P 12 bash -c "${PROCESS_IMAGE_FUNC_DEF}; process_image \"\$0\"") | |
| # Process results to determine if any actual thumbnails were created | |
| while IFS= read -r line; do | |
| if [[ "$line" == "CREATED" ]]; then | |
| ((actual_thumbs_created++)) | |
| fi | |
| done <<< "$results" | |
| # --- Cleanup and Final Messages --- | |
| if [[ $DRY_RUN -eq 0 ]]; then | |
| if [[ $actual_thumbs_created -eq 0 ]]; then | |
| if [[ -d "$OUTPUT_DIR" && -z "$(find "$OUTPUT_DIR" -maxdepth 0 -empty -print -quit 2>/dev/null)" ]]; then | |
| true | |
| elif [[ -d "$OUTPUT_DIR" ]]; then | |
| echo -e "${YELLOW}No new thumbnails created. Removing empty directory: $OUTPUT_DIR${NC}" | |
| rmdir "$OUTPUT_DIR" 2>/dev/null | |
| fi | |
| else | |
| echo -e "${GREEN}Successfully created $actual_thumbs_created new thumbnails in $OUTPUT_DIR.${NC}" | |
| fi | |
| else | |
| echo -e "${CYAN}Dry run complete. No files were modified.${NC}" | |
| fi | |
| exit 0 |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is ABSOLUTELY not my code. Generated originally with ChatGPT and then error checked and modified with Gemini.