Jeremy Wagner's

Web Development Blog

With occasional rambling diatribes about other stuff.

Bulk Image Optimization in Bash

March 9, 2017

This article is also on Medium!

Have you ever needed to optimize a bunch of images in a folder on your computer, but you don't want to go through the hassle of writing an build system to take care of it? Then bash and your image optimization binary of choice are your best friends. I've had situations where I just needed to pull down files from a website already in production, optimize images, and re-upload everything. The exact syntax depends on the optimizer you use, but your workhorse will be the find command.

find is a command that, well, finds stuff. For example, if I wanted to find all files in the current directory and its subdirectories with a .jpg extension, I could run this command:


find ./ -type f -name '*.jpg'

This will dump a list of files ending in the .jpg extension to the terminal like so:


./images/george.jpg
./images/frank.jpg
./images/estelle.jpg

In the above example, ./ is the current directory, -type f means we only want to list files (not directories, which incidentally we could find with -type d) and the -name parameter is the search pattern.

I was writing about image optimization in bash, right? Right, I was. Here comes the hook: The find command has an -exec parameter that you can use to pass file paths returned by find to other programs. What if you wanted to display the size of each JPEG file found by find?


find ./ -type f -name '*.jpg' -exec du -h {} \;

This would return output like this:


196K    ./images/george.jpg
16k     ./images/frank.jpg
28k     ./images/estelle.jpg

The -exec parameter uses a placeholder of {} to represent the file that was found with find. We can feed this placeholder into a command such as an image optimizer, and terminate the command with an escaped semicolon (don't forget!). From here, the sky's the limit. Here's a some examples of using find with different optimizers to replace unoptimized images with optimized ones.

Bulk JPEG optimization with jpeg-recompress

Here's a simple example of using the find command to optimize JPEGs with jpeg-recompress:


find ./ -type f -name '*.jpg' -exec jpeg-recompress {} {} \;

This one will run jpeg-recompress with the default settings. You'll see some improvement, but if you want to get more aggressive with your optimizations, you could try the following:


find ./ -type f -name '*.jpg' -exec jpeg-recompress -n 30 -x 75 -l 128 -a -s -c {} {} \;

This command will produce images with a JPEG quality between 30 (-n 30) and 75 (-x 75), loop over each image 128 times (-l 128), favor accuracy over speed (-a), strip metadata (-s), and then skip writing the output if no savings are realized (-c). Because of the amount of loops, this one could take some time, but if time is no object, then who cares?

Bulk PNG optimization with OptiPNG

For PNGs, I prefer OptiPNG. You can really go down the rabbit hole with all the options available, but the most effective (though it takes a lot of time) is this example:


find ./ -type f -name '*.png' -exec optipng -o7 {} \;

The -o7 flag will do 240 trials per image to see which is smallest. It's pretty ridiculous, but if you're doing this on your workstation (as opposed to on the fly in your web application) and you have time, why not? Run it overnight. If time is a factor, try the -o4 or -o5 setting, which only run 24 or 48 trials per image instead.

Bulk SVG optimization with svgo

svgo is the choice for SVG optimization. Using it with find to process a bunch of images is easy:


find ./ -type f -name '*.svg' -exec svgo {} \;

Easy enough, right? svgo is very fast too. Unless you have an absurd amount of images, you probably won't have to go make a pot of coffee while it churns away. If you want to get aggressive, you could lower the precision and enable multipass:


find ./ -type f -name '*.svg' -exec svgo -p 1 --multipass {} \;

If you decide to go to this extreme, I would highly recommend you examine the output. Lowering the precision of vector graphics isn't like lowering the quality setting of a raster image. If the precision of vector shapes is too low, they'll lose subtle (and sometimes not so subtle) details and look malformed.

Conclusion

find is great for a lot of things, especially optimizing images. It's important, though, that you remember a couple things about these examples: One, they overwrite images in their place, essentially clobbering them. So back stuff up before you run anything. Two, you may want to take a look at some of the output after you run the optimizer. Anecdotally, I've had no problems with these methods. The optimizers shown do a great job of shaving off kilobytes without adversely affecting image quality. Give this a shot with your favorite optimizers and share your secrets in the comments!

Cheers
-j

Thoughts? Let's hear them!