How to Create a Bash Image Resizer

How to Create a Bash Image Resizer

I once had a folder of a few hundred product photos, all at wildly inconsistent resolutions, that needed to be resized to a uniform dimension before uploading to an online store. Opening each one in an image editor would have taken hours. Instead, I wrote a small Bash script around ImageMagick, and what used to be an afternoon of tedious clicking became a five-second command. Here’s the full breakdown of how I built it.

Why Use Bash for Image Resizing

Bash itself doesn’t manipulate image data — that job belongs to dedicated image-processing libraries. But Bash is the perfect orchestration layer for:

  • Batch-processing hundreds or thousands of images with a single command.
  • Wiring image resizing into automated pipelines (upload processing, thumbnail generation, backups).
  • Combining resizing with renaming, format conversion, and compression in one pass.

The Tool Behind It: ImageMagick

The most common and powerful tool for this job is ImageMagick, specifically its convert (or newer magick) command.

Install it on Debian/Ubuntu:

sudo apt update
sudo apt install imagemagick

On Fedora:

sudo dnf install ImageMagick

On macOS (via Homebrew):

brew install imagemagick

The Simplest Resize Command

convert input.jpg -resize 800x600 output.jpg

This resizes input.jpg so it fits within an 800×600 bounding box while preserving the original aspect ratio (ImageMagick won’t distort the image unless you explicitly force exact dimensions).

A Basic Batch Resizer Script

#!/bin/bash

set -euo pipefail

target_size="800x600"
output_dir="resized"

mkdir -p "$output_dir"

for img in *.jpg *.png; do
    [ -e "$img" ] || continue
    convert "$img" -resize "$target_size" "$output_dir/$img"
    echo "Resized: $img"
done

echo "All images resized into '$output_dir/'"

How This Works Internally

  • mkdir -p "$output_dir" creates the output folder if it doesn’t already exist, without erroring if it does.
  • The for img in *.jpg *.png loop expands both glob patterns, catching common image formats.
  • convert "$img" -resize "$target_size" "$output_dir/$img" resizes each matched image and writes the result into the output directory using the same filename.
  • [ -e "$img" ] || continue guards against a glob pattern matching nothing (e.g., no .png files present).

Forcing Exact Dimensions (Ignoring Aspect Ratio)

Sometimes you need an exact width and height regardless of distortion, such as for a fixed-size thumbnail grid:

convert input.jpg -resize 300x300! output.jpg

The ! after the dimensions tells ImageMagick to ignore the original aspect ratio and force the exact size specified.

Resizing by Percentage

convert input.jpg -resize 50% output.jpg

This scales the image down to 50% of its original dimensions — useful when you want proportional resizing without specifying exact pixel values.

A More Advanced Script with Aspect Ratio Preservation and Quality Control

#!/bin/bash

set -euo pipefail

usage() {
    echo "Usage: $0 <width>x<height> <quality 1-100> <input_dir> <output_dir>"
    exit 1
}

if [ "$#" -ne 4 ]; then
    usage
fi

dimensions="$1"
quality="$2"
input_dir="$3"
output_dir="$4"

mkdir -p "$output_dir"

find "$input_dir" -maxdepth 1 -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" \) -print0 |
while IFS= read -r -d '' img; do
    filename=$(basename "$img")
    convert "$img" -resize "$dimensions" -quality "$quality" "$output_dir/$filename"
    echo "Processed: $filename"
done

echo "Batch resize complete."

Breaking Down the Key Parts

  • find ... -print0 combined with while IFS= read -r -d '' safely handles filenames containing spaces or special characters, unlike plain glob loops.
  • -iname makes the pattern match case-insensitively, catching both .jpg and .JPG.
  • -quality "$quality" controls JPEG compression quality (1–100), letting you balance file size against visual fidelity.
  • basename "$img" strips the directory path, leaving just the filename for constructing the output path.

Generating Thumbnails Alongside Full-Size Images

A common real-world pattern is generating both a resized “display” version and a small thumbnail:

#!/bin/bash

set -euo pipefail

input_dir="$1"
display_dir="display"
thumb_dir="thumbnails"

mkdir -p "$display_dir" "$thumb_dir"

find "$input_dir" -maxdepth 1 -type f -iname "*.jpg" -print0 |
while IFS= read -r -d '' img; do
    filename=$(basename "$img")
    convert "$img" -resize 1200x1200 "$display_dir/$filename"
    convert "$img" -resize 150x150 "$thumb_dir/$filename"
done

echo "Generated display and thumbnail versions."

This produces two output sets from a single source directory — a larger display-ready version and a small thumbnail — in one pass.

Converting Format While Resizing

ImageMagick can also convert formats as part of the same command:

convert input.png -resize 800x600 output.jpg

This resizes a PNG and outputs it as a JPEG in a single step, which is handy when standardizing a mixed-format image library.

Real-World Use Cases

  • E-commerce product photos: Standardizing dimensions across hundreds of product images before uploading to a storefront.
  • Website asset optimization: Generating responsive image sizes (thumbnail, medium, large) for faster page loads.
  • Photography workflows: Batch-resizing RAW-exported JPEGs for web galleries while preserving originals.
  • Social media prep: Resizing images to platform-specific dimensions before scheduled posting.
  • Automated content pipelines: Resizing user-uploaded images server-side as part of a web application’s processing pipeline.

Automation Example

Here’s a script I run via a folder-watching cron job to automatically resize new uploads as they arrive:

#!/bin/bash

set -euo pipefail

watch_dir="/data/uploads"
processed_dir="/data/uploads_resized"
log_file="/var/log/image_resizer.log"

mkdir -p "$processed_dir"

find "$watch_dir" -maxdepth 1 -type f -iname "*.jpg" -newer /tmp/last_run_marker -print0 |
while IFS= read -r -d '' img; do
    filename=$(basename "$img")
    convert "$img" -resize 1024x1024 -quality 85 "$processed_dir/$filename"
    echo "$(date '+%F %T') Resized $filename" >> "$log_file"
done

touch /tmp/last_run_marker

The -newer /tmp/last_run_marker flag ensures only files added since the last run are processed, making this safe to run repeatedly via cron without reprocessing everything each time.

Best Practices

  • Always output resized images to a separate directory rather than overwriting originals, to avoid irreversible quality loss.
  • Preserve aspect ratio by default; only force exact dimensions when you specifically need a fixed grid layout.
  • Use sensible JPEG quality settings (80–90 is usually a good balance between size and visual quality) rather than defaulting to 100.
  • Batch process using find -print0 and while read -d '' rather than plain globs, to safely handle unusual filenames.
  • Log processed files when running resizing as part of an unattended, automated pipeline.

Security Considerations

  • Malicious image files: Untrusted, user-uploaded images can sometimes exploit vulnerabilities in image-processing libraries. Keep ImageMagick updated to the latest patched version, and consider sandboxing processing of untrusted uploads.
  • ImageMagick policy restrictions: ImageMagick ships with a policy.xml file that can restrict certain operations (like reading remote URLs or specific formats) for security reasons — don’t disable these restrictions without understanding the risk, especially on servers processing public uploads.
  • Resource exhaustion: Extremely large or maliciously crafted images can consume excessive memory or CPU during processing (“image bombs”) — consider setting resource limits via ImageMagick’s -limit flag.
  • Path traversal: Sanitize filenames from untrusted sources before using them to construct output paths.

Optimization Tips

  • Use -strip to remove metadata (EXIF data, color profiles) from images, which can meaningfully reduce file size:
convert input.jpg -resize 800x600 -strip -quality 85 output.jpg
  • Process images in parallel using xargs -P to take advantage of multi-core systems:
find . -maxdepth 1 -iname "*.jpg" -print0 | xargs -0 -P 4 -I{} convert {} -resize 800x600 resized/{}
  • For very large batches, consider mogrify (which edits images in place, or into a specified output directory with -path) instead of convert, since it can be marginally faster for simple in-place batch operations.

Troubleshooting Common Issues

Problem: convert: command not found. Install ImageMagick via your package manager, and confirm with convert -version.

Problem: Resized images look distorted. You likely used the ! flag unintentionally, forcing exact dimensions and ignoring the original aspect ratio.

Problem: Output images are much larger than expected. Add -strip to remove unnecessary metadata, and adjust -quality to a lower value for JPEGs.

Problem: Script fails on filenames with spaces or special characters. Switch from a plain glob loop to find -print0 combined with while IFS= read -r -d ''.

Problem: “convert-im6.q16: not authorized” errors. This is usually due to ImageMagick’s security policy blocking certain formats (like PDF). Check /etc/ImageMagick-6/policy.xml and adjust cautiously if you understand the security implications.

Common Mistakes to Avoid

  • Overwriting original images without keeping a backup, resulting in permanent quality loss.
  • Forcing exact dimensions with ! when aspect-ratio-preserving resize is actually what’s needed.
  • Not stripping metadata, leading to unnecessarily large output files.
  • Processing untrusted user uploads without any resource or format restrictions in place.

Frequently Asked Questions

Does resizing reduce image quality? Downscaling generally preserves visual quality well; it’s aggressive JPEG compression (low -quality values) or upscaling that introduces noticeable quality loss.

Can I resize images without ImageMagick? Yes, alternatives include ffmpeg (also handles images) or vipsthumbnail from libvips, which is often faster for very large batches.

How do I resize only if the image is larger than the target size? Add a > after your dimensions: convert input.jpg -resize 800x600\> output.jpg, which only shrinks images larger than the target, leaving smaller ones untouched.

Can this resize animated GIFs? Yes, but you typically need the -coalesce flag before resizing to handle each frame correctly: convert input.gif -coalesce -resize 400x400 output.gif.

What’s the difference between convert and mogrify? convert creates a new output file, while mogrify modifies images in place (or writes to a directory specified with -path) — useful for quick batch edits without managing separate output filenames.

Summary

A Bash image resizer built around ImageMagick’s convert command scales from a single one-line command to a fully automated batch-processing pipeline capable of handling thumbnails, format conversion, and quality optimization. The details that matter most: preserve aspect ratio by default, strip unnecessary metadata to save space, use find -print0 for safe filename handling, and always keep your resized output separate from your originals.

References

  • ImageMagick official documentation: https://imagemagick.org/script/command-line-processing.php
  • ImageMagick resize geometry options: https://imagemagick.org/script/command-line-processing.php#geometry
  • Bash Reference Manual: https://www.gnu.org/software/bash/manual/bash.html
  • GNU find manual: https://www.gnu.org/software/findutils/manual/html_mono/find.html
Total
2
Shares

Leave a Reply

Previous Post
How to Generate QR Codes in Bash

How to Generate QR Codes in Bash

Next Post
How to Convert Text to Speech in Bash

How to Convert Text to Speech in Bash

Related Posts