brandur.org

Retina check

I try to put in retina versions of any JPGs or PNGs that I upload to this site for higher definition screens. These high resolution assets are given a filename ending with @2x, and Retina.js swaps them in for the standard image assets as a page is loading. Occasionally though, I forget to upload those retina assets, or just get lazy.

I put a fun little script in CI last week to check that all scalar images have retina compatible versions. If an image comes in on a pull request to publish a new article, the build fails and I get a chance to go fix it.

One quirk that I had to build in was a whitelisting system for the existing set of images that I have which don’t have retina companions. I wanted to take my set of “bad” images (A), exclude any found in the whitelist (B), and print the remainder as those that need fixing. It turns out that in Bash array operations are a hugely non-trivial operation, and I ended up with quite an epic hack to work around the limitation:

bad_images=$(echo "${allowed_exceptions[@]} ${bad_images[@]}" | tr ' ' '\n' | sort | uniq -u)

Which produces the A - B set operation that I wanted 1. It works by printing both sets, changing the item delimiter to a new line, sorting the result, and using uniq with the unusual -u operation to produce the result. -u is the secret sauce: it changes uniq’s standard behavior to only printing lines that are not repeated in the input; in this case uncovering any filenames that weren’t in both sets A and B.

The next time someone tells you that Bash represents the culmination of the elegance that is the Unix philosophy, hit them with this set exclusion problem. If they can write a script without Google’s help that doesn’t fall back on a O(n^2) operation involving nested loops, then you’re dealing with a truly fearful programmer indeed.

1 Actually, A ⊕ B, but that still works out nicely here.

Did I make a mistake? Please consider sending a pull request.