42 Astounding Scripts

MacOS uses Perl, Python, AppleScript, and Automator and you can write scripts in all of these. Build a talking alarm. Roll dice. Preflight your social media comments. Play music and create ASCII art. Get your retro on and bring your Macintosh into the world of tomorrow with 42 Astoundingly Useful Scripts and Automations for the Macintosh!

Jerry Stratton

This is the fun part of being a chef. You get to play with your food. — Walter Staib (A Taste of History: From Kitchen Garden to Table)

Have yourself a musical command line…—Wednesday, December 22nd, 2021
Merry scriptmas

Merry Christmas! It’s time for another programming toy under your weekend scripter Christmas tree. And what could be more Christmasy than a script that doesn’t just play music but is music?

If you’ve read 42 Astounding Scripts (and if you haven’t, the ebook is on sale until the end of the year) you’re familiar with the “shebang line”, that pound-exclamation line at the top of every script. It usually looks like #!/usr/bin/something. It tells the computer what scripting language interpreter should run this script.1

A scripting language interpreter is nothing more than a command-line program that accepts files as input and interprets them as code to do something. When you put a shebang line at the top of a file and mark that file as executable, you’re telling the computer that whenever you run this script you want it to run the script file through the interpreter named in the shebang line.

If you put #!/usr/bin/python at the top of the script file, that means it should be interpreted as Python code, by running whatever program is at /usr/bin/python. If you put #!/usr/bin/perl, it should be interpreted as Perl code, by running whatever program is at /usr/bin/perl.

But that’s all the shebang line is doing: it’s telling the computer that this file must go through the interpreter at that path. The interpreter interprets the code in the file, nothing more than that. As long as the program specified in the shebang can handle getting the file as input, it will work. This is why most scripting languages, even those that use other characters for comments such as // or /* … */, also accept the pound symbol for comments: so that they won’t be confused by the shebang line if they’re called as a shell scripting language. Even AppleScript handles pound signs as comments, specifically for that reason.2

The piano script from 42 Astounding Scripts can accept files of notes as input. It can ignore lines beginning with hashes. The piano script can be used as a shebang line interpreter.

Regenerate multiple files—Wednesday, October 6th, 2021
Bit pipes

This is sort of a minor codicil to a handful of much more interesting posts about automating the generation of Daredevils RPG character sheets over on the Biblyon Broadsheet.

At a role-playing game convention recently, I had a whole bunch of pregenerated characters, and regularly needed to recreate their character sheets from source files after making changes to either the characters or to how I interpreted the character generation rules. I used a Daredevils pregen calculator to do this. But while the calculator could take multiple inputs and produce a single file with every character in it, I ended up choosing to have a separate output file for each character. It was easier to find any particular character that way.

This meant having to rerun the script for multiple characters every time I made changes. That’s obviously a target for automation. What I needed was a script to check each source file against its generated character sheet—and against the ever-changing daredevils script—and run the script on that source file if it’s newer than the sheet or if the destination is older than the script that generated it.

The “file” command could probably handle this for me. But it is an arcane command which is not amenable to obviousness. It produces long command lines with obscure switches that require, for me at least, reading through the man page every time I need to alter it. I need more obviousness in my automations.1

So I wrote a script that takes files on the command line, a directory for the output files, and a script to run on each file.

The basic requirements are:

  1. a bunch of input files, such as my character stats files for Daredevils;
  2. the same number of output files in a separate but common directory, with the same name (sans any extension) and the extension “.txt”;2
  3. a script that runs on the input files and produces text to standard output; the standard output for an input file is piped to the corresponding output .txt file.

The script checks whether the input file is younger than the output file before running or if the output file is older than the script. In the former case, the data has changed and the output needs to be recreated. In the latter case, the logic has changed, and the output needs to be recreated.

I haven’t needed a --force option yet, but it’s an obvious potential add.

Caption this! Add captions to image files—Wednesday, September 22nd, 2021
Let them eat cake

One of the coolest scripts in 42 Astoundingly Useful Scripts and Automations for the Macintosh takes an image and creates ASCII art from the image. The ability to easily manipulate images at high quality is one of the great features of scripting on macOS. Even when the purpose is to produce a low-quality, retro image, as that script does.

Many of the image manipulation tasks I do, I do almost but not quite the same thing every time; it’s mind-numbingly dull in a GUI app such as GIMP or Inkscape to add a caption, for example, to the bottom or top of an image.

It’s fascinating turned into a script.

That I find things more fascinating when I can manipulate them on the command line than I do playing around with a mouse and menus may be one of the tells that make a weekend scripter. That’s a topic for another day. Today’s script, caption (Zip file, 6.0 KB), takes an image and adds a caption to it. It attempts useful defaults for everything it can, and provides command line switches for those defaults that are either difficult (or dangerous) for a computer to guess or that might need to occasionally deviate from the most likely guess.

I wrote it in Swift because that’s the easiest way to access the image manipulation libraries built in to macOS. The script can add a caption above or below (the default) an image, or layer it over an image. It can add borders, and it can resize the image proportionally, outputting it as PDF, JPG, PNG, or TIFF.

Suppose I want to put the Gettysburg Address as a caption below a photo of the Lincoln Memorial.

[toggle code]

  • caption Lincoln.jpg --output Gettysburg.jpg  < address.txt

The original image is “Lincoln.jpg”. The new file will be named “Gettysburg.jpg”. And it will get its text from the file “address.txt”.

Because this is long text, the script will justify it instead of centering it.

The Gettysburg Address

The origin of this script came when I wanted to caption a photo of the last slice of pie as a joke art piece.1

  • caption pie.JPG --output "Last Slice of Pie.jpg" --align right < pie.txt
Using version control with AppleScripts—Wednesday, July 21st, 2021
Save Clipboard changes in Mercurial

Tracking an AppleScript in Mercurial.

In 42 Astoundingly Useful Scripts and Automations for the Macintosh I devoted one diversion to the importance of version control. One thing I left unmentioned except through omission is that AppleScripts created with Script Editor (as most will be, due to Script Editor’s verification and testing ability) can’t really be tracked in version control. They aren’t text files, so while changes will be noted, what those changes are will not. Change tracking pretty much requires line-oriented text files, and Script Editor files are not text files, at least in any sense meaningful to Mercurial or Git.

I have a lot of AppleScripts in my User Scripts folder for several applications, as well as a handful of AppleScripts in my Finder window toolbars and my Favorites folder. I’ve always been disappointed that I can’t track changes to them in Mercurial. And I’ve always been worried that I don’t have a backup of them in easily-readable text format.

While working on this problem, I noticed that I have a lot of scripts I’ll never read again because the format is no longer valid on macOS.

I’ve considered, and occasionally tried, keeping two copies of every script I write: when I’m done editing a script in Script Editor, copy the text to a text file and save that as a readable, future-proofed version. It always works for a very short period and then I forget, and the two sets of scripts get out of sync. As I was writing this post, I discovered an abandoned .hg folder in my user scripts folder, last touched on September 20, 2014.

It occurred to me while writing the Save Clipboard script that there are so many commands beginning with osa there must be one for getting the text of an AppleScript out of a .scpt file or .app folder.1 And to ask the question is to answer it: osadecompile does exactly that. This makes it trivial to write a script that keeps the two locations—the live location and the text repository—in sync. The text backup can then be tracked easily in any version control system, including Mercurial.

Older posts