Nathan Grigg

Some AppleScript tips

Last week I was feeling tired of dealing with random AppleScript errors and bugs caused by my complete lack of understanding of the language. I ran across Apple’s AppleScript Language Guide (pdf), which gives a good overview of the language and explains why some things happen they way they do.

I am by no means an AppleScript expert, but here are a few useful things I learned from reading the guide. If you ever AppleScript, set aside an hour to read the guide.

POSIX file, file, and alias

There are three ways to specify a file in AppleScript

alias "Macintosh HD:Users:grigg:Desktop:notes.txt"
POSIX file "/Users/grigg/Desktop/notes.txt"
file "Macintosh HD:Users:grigg:Desktop:notes.txt"

An alias can only refer to a file that already exists. Once an alias is defined during the run of a script, it will refer to the same file even if it is moved or renamed.

Both POSIX file and file refer to filesystem locations instead of files. If you want to refer to a file which does not yet exist, you must use one of these. The only difference between the two is that POSIX file requires paths in POSIX notation, and file requires paths in classic Mac notation.

If you need an alias but prefer to specify paths in POSIX form, you can use

POSIX file "/Users/grigg/Desktop" as alias

If you have an alias or file and want the POSIX name, use POSIX path of the object.

Relative paths are not supported (except when run from a shell; see below) and tilde expansion is not supported.

Run AppleScripts as shell scripts

If you want to run an AppleScript from the command line, you can save the AppleScript as plain text with the interpreter listed in the first line:

#! /usr/bin/osascript

Then make it executable using chmod +x, and run it like you would any other shell script. Of course, your system will need to compile such scripts each time they are run, which in my quick experiment added only 10 to 30 milliseconds of overhead.

If you want to store your AppleScript as plain text but run a compiled version, you can use osacompile. If script.applescript is saved as plain text, then

osacompile -o script.scpt script.applescript

saves a compiled version as a binary file script.scpt. To run the compiled version, you will need to run osascript script.scpt.

AppleScripts run from the command line print their return value (or the result of the last expression, if there is no explicit return) to the output stream.

AppleScripts run from the command line can accept arguments (which are passed as a list to the run handler) and can use relative paths. The following script prints the full Mac-style path to the file listed as first argument.

#! /usr/bin/osascript

on run(arguments)
    set filename to POSIX file (first item of arguments) as alias
    return filename as string
end run

Use pipes as variable delimiters

AppleScript allows you to define variables using pipes as delimiters, for example |variable|, |my variable|, or |2^5|. Naming a variable |2^5| is probably not a good idea, but pipes can still be useful because of the way the AppleScript Editor treats them.

AppleScript reserves a lot of words for its own use. If you are sending commands to another application, this will also reserve a bunch of words. So if you need a variable name, there is a good chance you will accidentally use something you aren’t supposed to, which results in cryptic and annoying errors. Some people get around this by prefixing variables with my or the, as in theFile or myInteger. I have been known to use excessive underscores.

Another way around this issue is to use bars. The best thing is that AppleScript Editor removes them if they are not necessary. So you you write |variable| and nobody else is claiming this word, the editor will remove the bars when you compile or run, giving you confirmation that you are free to use this word.

Reference to a variable

AppleScript deals with variables pretty much the same as Python. Lists and records (dictionaries) are mutable and passed by reference. Strings and numbers are immutable and essentially passed by value.

For example,

set a to {1,2}
set b to a
copy 3 to end of a
-- now a and b are both {1,2,3}

You can use copy a to b to create a deep copy or set b to items of a to create a shallow copy.

In AppleScript you can force pass by reference using the term a reference to.

on change_to_5(b)
    set contents of b to 5
end change_to_5

set a to 0
change_to_5(a reference to a)
-- Now a = 5

This can also be useful when dealing with long lists. According to the Language Guide, the most efficient way to append to a list is

copy 5 to end of (a reference to long_list)

I don’t understand why.

Extract elements of a given type from a list

I don’t know if I would ever actually use this, but I thought it was interesting. If you write

set mixed_set to {1, 1.2, 3, "hello", 5}
integers of mixed_set

then AppleScript returns {1, 3, 5}. You can also write integer 3 of mixed_set to get 5.