Web & Software Developer

Bash Shell Script To Bulk Optimize Images With TinyPNG API

This is a bash shell script that will optimize and compresses all JPG, JPEG, and PNG images in a local directory with the TinyPNG API. It will optimize images with these extensions: .png, .PNG, .jpg, .JPG, .jpeg, and .JPEG.

The script will not modify or delete your existing images. It will simply create an optimized copy of all the images in the directory that you specify, and it will place the optimized images in a new directory. The new directory will be named the same as the specified directory, but with the added suffix, “_tiny”. So, if you want to optimize a folder of images named “uploads”, the new folder of optimized images will be named “uploads_tiny”.

The point being that your original folder of images remains intact, with images in their original size. You’ll simply have a new folder of optimized images.

Required: you must have your TinyPNG API key.

NOTE: This script uses the TinyPNG API, and they allow a maximum of 500 images to be optimized per month, unless you have signed up with them to pay for optimizing extra images. Obviously, this script will only optimize the maximum number of images allowed per month by your TinyPNG API key.

  1. In the script (the script is below), on line 2, replace YOUR_API_KEY with your own TinyPNG API key. (Keep that key secret!)
  2. Copy the script and use any text editor to paste the script into a new file. Save the file with the name tinypng.sh
  3. Place the script in the same directory as the directory of images that you want to optimize. So, if the folder that you want to optimize with TinyPNG is called “uploads” and it’s in your “Documents” folder, then you should place the tinypng.sh script in the “Documents” folder.

    It doesn’t matter if other folders or files exist there, they’ll be ignored.

  4. To optimize the images, run the script. To run the script, you have to specify the name of the folder of images that you want optimized.

    First, open a terminal and navigate to the directory of the script. In this example, it’s the Documents folder:

    cd Documents 

    Run the script, specifying the name of the folder of images that you want to optimize. In this example, the folder of images is called “uploads”:

    ./tinypng.sh uploads 

    As another example, you could optimize only a sub-directory, like this:

    ./tinypng.sh uploads/subdir 

    If all goes well, you will end up with a new folder named, “uploads_tiny” (or “subdir_tiny” if you used the latter example) which contains all of the images optimized with TinyPNG. When the script is complete, it will give you a summary that tells how many files are in the original folder and how many files are in the new folder. That way, you can be sure that all the images were optimized. The end of the script also tells you the total size of both folders so you can see how much disk space you save by using TinyPNG API to optimize your images.

The Script


# Make sure source dir is supplied
if [ -z "$1" ]
	echo "Missing argument. Supply the source directory containing the images to be optimized. Usage: ./tiny.sh <source_dir>"
	exit 1


# Make sure source dir exists
if [ ! -d "$INDIR" ]; then
	echo ""$INDIR" directory not found. Supply the source directory containing the images to be optimized. Source directory should be relative to location of this script. Usage: ./tiny.sh <source_dir>"
	exit 1


# Make sure output dir does not already exist with files.
# If dir exists but empty, it's ok, we proceed.
if find "$OUTDIRNAME" -mindepth 1 -print -quit | grep -q .; then
	echo "Output directory ($OUTDIRNAME) already exists. Exiting without optimizing images."
	exit 1

# Create output dir if it does not already exist
if [ ! -d "$OUTDIRNAME" ]; then
	echo "Creating output dir "$OUTDIRNAME"..."

# Begin optimizing images
echo "Optimizing images..."
shopt -s nullglob
for file in *.png *.PNG *.jpg *.JPG *.jpeg *.JPEG
	Cfile=`curl https://api.tinify.com/shrink --user api:$TINYAPIKEY --data-binary @"${file}" --dump-header /dev/stdout --silent | grep Location | awk '{print $2 }'`
	Cfile=${Cfile// }
	Cfile=`echo -n "$Cfile"| sed s/.$//`
	curl ${Cfile} -o "${OUTDIR}/${file}" --silent

# Gather stats
echo "Gathering stats..."
INDIR_SIZE="$(du -h)"
INDIR_FILE_COUNT="$(ls | wc -l)"
OUTDIR_SIZE="$(du -h)"
OUTDIR_FILE_COUNT="$(ls | wc -l)"
echo -e "Finished.rnOriginal directory ($INDIR) has $INDIR_FILE_COUNT files; total size $INDIR_SIZErnTinified directory ($OUTDIRNAME) has $OUTDIR_FILE_COUNT files; total size $OUTDIR_SIZE"


We've 2 Responses

  1. April 10th, 2017 at 6:17 pm


    Many thanks for this; it’s proven very useful on an ad-hoc basis!

    I wonder – would it be possible to extend the script to allow it to:

    – Recursively parse through a collection of nested sub-directories (all of which live under a parent dir)
    – Within each branch, look for a particular sub-dir name (i.e. RAW)
    – If found, optimise any images found within (rename original to _orig and then create a new optimised image with the orignal file name)

    This is proving to be beyond my meagre skills!

    Deeply appreciate any response!

  2. March 14th, 2018 at 5:33 pm


    For MAC OS environment I needed to change the hook word that you used in grep from Location to Location. Without it he can not make the cut correctly


Questions and Comments are Welcome

Your email address will not be published. All comments will be moderated.

Please wrap code in "code" bracket tags like this: