Mimsy Were the Borogoves

Hacks: Articles about programming in Python, Perl, Swift, BASIC, and whatever else I happen to feel like hacking at.

AppleScript as shell scripting language

Jerry Stratton, September 10, 2013

I just put two very obvious things together that I always knew, but hadn’t thought about at the same time. Since /usr/bin/osascript can send arguments to AppleScript text scripts, and since the shell automatically runs scripts through the first comment-bang if it exists as an executable, AppleScript can be used from the command line without even needing things like the long-dormant appscript.

[toggle code]

  • #!/usr/bin/osascript
  • on run names
    • repeat with name in names
      • log "Hey, " & name
    • end repeat
  • end run

Then, run it with some arguments, and:

  • $ bin/hey Fred Wilma Barney
  • Hey, Fred
  • Hey, Wilma
  • Hey, Barney

This even runs without a logged-in user. Now, obviously, with the strength of AppleScript being contacting running applications, using this without a logged-in session isn’t going to be a common solution.

Here, for example, is a script that uses Nisus Writer Pro to educate text in a text file, using Nisus Writer’s Plain to Smart quotes menu item:

[toggle code]

  • #!/usr/bin/osascript
  • -- educate text using Nisus
  • on run arguments
    • set workingDirectory to do shell script "pwd"
    • set results to ""
    • repeat with flatfile in arguments
      • set currentFile to workingDirectory & "/" & flatfile
      • set currentFile to POSIX file currentFile
      • log currentFile
      • set currentText to theContents for currentFile
      • tell application "Nisus Writer Pro"
        • set workspace to make new document
        • set text of workspace to currentText
        • --if Nisus had to start up, we need to activate it to get rid of the About window
        • if name of window 1 is "About Nisus Writer Pro" then
          • activate
        • end if
        • Do Menu Macro with macro "Edit:Select:Select All"
        • Do Menu Macro with macro "Edit:Convert:Plain Quotes to Smart Quotes"
        • set educatedText to text of workspace as text
        • set results to results & "\n" & educatedText
        • --display dialog educatedText
        • close workspace saving no
      • end tell
    • end repeat
    • copy results to stdout
  • end run
  • --read a file and return its contents
  • on theContents for theFile
    • open for access theFile
    • set fileContents to read theFile as «class utf8»
    • close access theFile
    • return fileContents
  • end theContents

A couple of tricks and notes:

  • It uses the “log” command to log to standard error.
  • It uses “copy variable to stdout” to write to standard output. However, in AppleScript, stdout is some sort of pseudo variable; it only holds the last “copy to” and it cannot be appended to because it isn’t defined. So I’m saving the output text in a variable and then copying that variable at the end of the script.1
  • There might be a better way to get the current working directory inside of AppleScript, but I don’t know what it is. It feels kind of like cheating to get it by using “do shell script”. This is a shell script.
  • The “Do Menu Macro” commands don’t target a specific window. They’ll take the top one, which should be the one we just created. Currently, Nisus must have been activated or visited at least once for Select All to work. Otherwise, it sticks on the “About” window that applications often display on startup. This script only activates Nisus if the About window is the first window; this means that it only pulls out of Terminal if Nisus had to start up. Otherwise, there is no need for a focus shift.
  • I’m assuming that all files I’m looking at will be in UTF-8, so I’m specifying that when I read it in. If you leave out the “as «class utf8»” then AppleScript assumes something else, I think MacRoman.
  • Display Dialog works, too. I used it here for testing whether I was successfully getting the educated text back from Nisus. It only works if it’s in another application’s context, however, such as here where it is in Nisus’s context.

So, if I have a text file that looks like:

It was just another piece of spam that most people never read. If they saw it at all, they complained to their assistant about letting spam through. "But it was signed by… I don't know who it was signed by. Sorry."

"Hi!" said the message. "I'm your new, improved Neon Goddess! You may worship me any time you might worship Yahweh, Allah, Jesus, or any of your secular humanist philosophies. I am fully compatible with clasping of hands, scraping, bowing, and chanting."

"As Mother has written, knowledge and the pursuit of knowledge is dangerous. Leave knowledge to me. Swords shall be beaten into pens and pens into plows, and you shall sing for joy. Drop your wishes into the bit bucket before the miracle occurs one lucky winner will be chosen for a wish-granting. You may now return to your regularly scheduled calendar."

"Oh yes, I can hear what you're saying, so watch out."

"Thank you for listening, and aloha."

I can run

  • ~/bin/eduscript Dumb\ Quotes.txt > Smart\ Quotes.txt

And end up with:

It was just another piece of spam that most people never read. If they saw it at all, they complained to their assistant about letting spam through. “But it was signed by… I don’t know who it was signed by. Sorry.”

“Hi!” said the message. “I’m your new, improved Neon Goddess! You may worship me any time you might worship Yahweh, Allah, Jesus, or any of your secular humanist philosophies. I am fully compatible with clasping of hands, scraping, bowing, and chanting.”

“As Mother has written, knowledge and the pursuit of knowledge is dangerous. Leave knowledge to me. Swords shall be beaten into pens and pens into plows, and you shall sing for joy. Drop your wishes into the bit bucket before the miracle occurs one lucky winner will be chosen for a wish-granting. You may now return to your regularly scheduled calendar.”

“Oh yes, I can hear what you’re saying, so watch out.”

“Thank you for listening, and aloha.”

This ought to be a handy tool when you want to integrate a GUI application into your command-line workflow.

  1. Neat trick: you can rename the “results” variable to “stdout” in this script. It will work fine. You will still need “copy stdout to stdout” at the end to get the data outputted to the command line.

  1. <- ajax_selects custom forms
  2. Pipe progress viewer ->