Notes on AppleScript Programming

Notes on Scripting and Automation using AppleScript

Keywords: AppleScript, scripting, automation, application glue, Mac, MacOS


Disclaimer

I first wrote this more than a year ago, as part of the set of documents to help people at Genome Therapeutics figure out what I did on my contract assignment. Later, I thought that some ideas or tips here could be interesting to a larger audience, and so I set to reformat it in HTML and put it here. However, since it's been more than a year, and I don't have a Mac handy, some of it may be outdated.

Revision history

Abstract

This document presents techniques and considerations for developing AppleScript scripts for automating various behaviors of applications. It presents a way of approaching automating applications with AppleScript, that ambitiously attempts to show that needs to be done can be done. This is the real value of this document.

However, for those with little or no familiarity with AppleScript, the document first introduces the general concepts and informative references. Also, for beginners, this document describes some issues that are immediately questionable by AppleScript novices yet are not immediately obvious from books and manuals.

The format and structure of this document is more like that of a Knowledge Base or a FAQ (where questions are not so much "Frequently Asked", as "Thought to be frequently asked by the author of the document"), than of a coherent manual, and should be read as such. For this reason such a relatively short document has a table of contents. To be honest, the most valuable part of this document just may be the References section...


Table of contents

  1. General Introduction to AppleScript
    1. AppleScript language, English dialect
    2. Some definitions
    3. Libraries and code reuse
    4. Extensions, additions and precedence
    5. Dictionary and terminology
  2. AppleScript Programming Patterns and Tricks
    1. Scriptability and recordability
    2. PreFab Player
    3. Attachability
    4. Control Panels
    5. Passing Data Around
    6. Info About a File
    7. Simulating User Input
    8. The MacGyver approach
    9. Creative flow control
    10. Extended character set
    11. Memory and performance
    12. Scheduling
    13. Script Editor's limitations
    14. Kitchen sink
    15. Java
    16. Different versions of MacOS
    17. Other random (but important) notes
  3. Footnotes
  4. References
    1. Online
    2. Books

General Introduction to AppleScript

AppleScript language, English dialect

A language is a dialect with an army and a navy.
(somewhere seen attributed to Max Weinreich, of whom I don't know anyway)

This is not a frivolous section, regardless of what you may think after reading the next few sentences. The truly frivolous (but still one with a message) section is here.

It is well known that Unix and C are a practical joke that got out of hand. Indeed, the above source quotes Brian W. Kernighan as saying:

We stopped when we got a clean compile on the following syntax:
for(;P("\n"),R-;P("|"))for(e=C;e-;P("_"+(*u++/8)%2))P("| "+(*u/4)%2);

Why is it mentioned here? The above does not look at all frightening to a (somewhat seasoned) programmer[1]; instead, it seems peculiar when the syntax is as follows (AppleScript 1.5 and later, meaning, I believe, MacOS 9.1 and up):

round some number of {3.1415, 2.718, 4.33, 90.9} rounding as taught in school

This alone gives one an idea of the AppleScript syntax. (Similar sort of thinking probably went into the design of SQL, especially with clauses such as HAVING.) An AppleScript dialect is a version of AppleScript based on the words and syntax of a particular language (a human language or a computer language). The English dialect is based on the English language. AppleScript can resemble any language, in theory; currently, only English dialect is supported, and, according to Apple, "there is currently no plan to support dialects other than English."

See also:

Some definitions

Some definitions and explanations to keep in mind.
  1. A modal dialog is a dialog which is usually displayed in the frontmost window, and has to be responded to before any other windows can be accessed. This is a bit vague, as the "modality" actually changes depending on the application in question. E.g., sometimes some menu items are accessible, sometimes the dialog is only "modal" with respect to the given application, but other applications can be accessed freely, etc. Unfortunately, there is no accepted terminology (at least to my limited knowledge) which would allow one to speak, for instance, of a first-degree modal dialog (which precludes any interaction except with the dialog), a second-degree modal dialog (which allows interaction with other applications), a third-degree modal dialog (which allows some menu commands to be accessed), and, perhaps, others.

    Obviously, modal dialogs are an important issue to consider when scripting an application.

    See also:

  2. Apple Events are messages through which AppleScript and scriptable applications communicate with each other.

  3. Finder. All Macs are configured are configured to boot into the Finder of the OS, which is the GUI shell application. The Finder is somewhat analogous to the combination of File Manager and Explorer in the Windows realm. It manages the desktop, displaying icons for each file, mounted volume, and other devices.

  4. Scripts, or applets, etc. Scripts can be saved by the Script Editor as text (useful, for example, when reading the text of a script on a non-Mac platform), as a compiled script, or as an "applet". The applet may be run as a stand-alone application, without requiring Script Editor or another development environment to run it.

Libraries and code reuse

A script may define subroutines or classes, which are available to other scripts (An AppleScript applet is thus inherently scriptable). Thus libraries of AppleScript subroutines and classes can be built.

Such libraries may be used in two ways: by loading them into an applet that uses them (think C preprocessor’s #include or, better yet, Perl’s use), or by calling them like other applications, by using tell.

An applet can be saved as "Stay open", in which case it will not exit after the main loop (if any) has completed execution. It can be useful for applets that sit idle until an event occurs to which they react. A useful optimization, thus, is saving "library" scripts in a "Stay-open" mode, since it loads the library into memory once and further calls are faster.

In some cases, including the library script as a parent makes sense. For details, see this column, again by Bill Briggs.

Script objects and libraries can also be accessed remotely via EPPC protocol (i.e., through URLs such as eppc://boo.hoo.com/runme), which runs on top of TCP/IP.

See also:

Extensions, additions and precedence

One will hear talk of extensions and additions. Extensions are better described here. In short, extensions extend (duh!) the ability of the Macintosh operating system to do something. They are not strictly necessary to the operation of the OS. In fact, the OS can be started with extensions off (and each extension may be turned on or off individually).

Scripting additions, aka OSAX (Open Scripting Architecture eXtensions) are a sort of library (written in C, for example), that extend basic AppleScript functionality by offering ways to, for example, create complex dialogs, simulate user input, etc.

Both scripting additions and extensions extend the set of commands available to AppleScript scripts. Whether a command in a given context in a script taken to be a call to an application, a call to a script’s own subroutine, a call to AppleScript’s function or a call to (one or more) scripting addition’s methods is a frequent point of confusion and bugs. This column explains it clearly enough. This may not make sense at this point in the document, but is helpful when reading and writing AppleScript.

Dictionary and terminology

A terminology of an application (or a scripting addition) is a set of classes and events of an application. The term may probably, in some contexts, be used interchangeably with dictionary, although to be purist a dictionary is a list of commands and objects, with syntax and short documentation, in a human-readable form. By contrast, terminology is the mapping of dictionary commands to internal commands. Or so I think. I have never seen them rigorously defined anywhere. In general, people speak of using terminology to script an application and of reading a dictionary to find out the terminology. One can say that dictionaries and terminologies specify an application’s API. "But the idea is the important thing!"

If an application does not have a dictionary, it may still be scriptable; but raw syntax must be used to represent data and commands.

See also:

AppleScript Programming Patterns and Tricks

Scriptability, recordability, attachability

An application can be scriptable without being recordable, but obviously not vice versa. This makes it harder to understand what exactly does the application offer in terms of scriptability, especially with lack of documentation, such as is the case with some applications. This means that the only readily available source of information is a dictionary, from which only syntax and short description of commands can be gleaned.

Extensive scriptability is the easiest way to tell an application what to do. In the absence of that, as the overview points out, some applications have workarounds by letting you call their in-built macros or menus. Some applications, for example, offer very little in the way of scriptability, but at least it offers methods for pressing buttons and entering keystrokes in dialogs and choosing menu commands (and even this functionality is not complete - many a time there are a number of dialogs with non-scriptable buttons in.)

But there is another way of telling even staunchly nonscriptable applications what to do, as well as getting information from them. Scripting additions exist to allow applets to simulate mouse movements and clicks as well as keyboard input. Consider the availability of keyboard shortcuts for menu options and other functions even in non-scriptable applications - and more opportunities open. Cutting and pasting between the application and clipboard accessible to AppleScript is also possible, thus enabling applets in many cases to communicate with applications in a more interactive, rather than batch, mode. For all this, including drawbacks of such approaches, problems that they present and ways to overcome them see section “Other scripting tips and tricks” below.

PreFab Player

PreFab Player is a good addition that, in the words of its authors, allows your script to "Directly control any application, desk accessory or control panel through its user interface." Further, they mention, just as we do, that "A few applications are very scriptable, but still leave small and occasionally vital gaps. Many other applications have added modest support for scripting, but left many important features untouched. Player fills these gaps. Nearly anything that can be done by hand can now be done from a script."

Sounds familiar? It’s a very useful tool, and not very expensive ($139). But let us see how it’s doing its business? It turns out that it "...mimics user input, choosing from menus, selecting radio buttons, typing text, clicking buttons, and more." Well, the same functionality can be achieved by combining FREE (or cheap shareware) third-party tools such as scripting additions (that simulate mouse and keyboard inputs) and control panels with tricks mentioned below. If you meet a stubborn application with modal dialogs, don’t expect miracles from Player.

See also:

Attachability

Attachability is another related feature. It does what can be described as, to some extent, the opposite of scripting. Rather than AppleScript driving an application, AppleScript applets are inserted as menu options into an application. This is a way to customize apps, not to automate them. Obviously, though, an application thus enhanced is in turn better scriptable, and so better automated.

Control Panels

Control panels allow the user to set (persistently, until reset by the next invocation of the control panel) a variety of system behaviors or properties (for example, TCP/IP settings, such as gateway, DNS server addresses, etc.). Some may be useful for scripts, which may depend on a certain behavior but are not able to influence it. For example, Okey Dokey Pro control panel allows one to set a timeout for dialogs for a set of applications. If one of the applications in the set opens a dialog, the dialog stays open only for the specified timeout time; afterwards it closes with the result of pressing default button ("Save", "OK", etc.). This is very useful for scripting applications with modal dialogs.

Passing Data Around

Data can be passed among scripts and applications in a way a user does it - by using cut and paste (see XXX) to understand how to work with the clipboard - yes, the object is the clipboard - for cutting and pasting). For temporary storage of these short snippets of text, standard
Stickies application can be used..

See also:

Info about a file

MacOS stores file info in two forks - a data fork, which contains the info that the file normally would contain on other systems, and a resource fork, which contains MacOS-specific info for the file (such as its icon, a code for an application that created the file - very useful, and may be even better than using DOS-like extensions, etc.)

Finder also stores some information about files separately, in Finder Info.. This should be kept in mind when files are to be placed on other file systems; if one relies on the information in resource fork for further processing, say, on Unix, one needs to make sure the resource fork is copied, and use a parser to get resources on non-Mac systems.

Labels are a useful example of the latter. Files and folders in MacOS have labels, which can be set to one of several colors. The label color can be set and queried through a call to Finder, and can be used to encode some information about a file and have the file system persisting the data. For example, a transparent label may be used to signify that the file has not been processed, a red ("Hot") label - that there was something wrong with the file, and blue ("Cool") label - that file has been processed successfully.

To reiterate: The label is not a part of the resource fork of a file or a folder, instead, it’s maintained by Finder, which makes it impossible to figure out a label by looking at a file from another OS. That is, when writing onto a mounted Unix volume, labels can be used, but cannot be determined by looking at the files from within the Unix system, even when the resource fork is copied. Finder Info must be copied as well.

See also:

Simulating user input

As mentioned above, sometimes one must resort to simulating mouse movements and clicks as well as keyboard input. Numerous scripting additions are available for this purpose. However, with mouse clicks an obvious problem arises: how to guarantee that the coordinates of the click will be relevant every time, since we may have no control of how the application places its windows. At least two solutions are possible:

Of course, if the window’s content is bigger than the window itself, and one needs to scroll down or right to find the necessary place to click, the problem becomes a bit more challenging. Be creative, however. You can, for example: simulate a mouse dragging a scrollbar, or, better yet, simulate a PageDown keystroke.

Moreover, sometimes this is not necessary to handle large windows. A lot of such windows may display lists of items (such as directory contents) in a list that can, of course, grow large. But they may also provide a clickable headings for every column of the list, which resort the list by the values of the column. An application in question may very well also provide a "Search" function to get only the items of interest selected. Thus clicking on these items of interest directly is not necessary -- simple simulation of a Returnkeystroke, or of an "Open”" menu command, may suffice. You may also use Reverse Selection menu command, often available, to then remove all items you do not care about from the current view.

Additionally, I have recently found out about Window Manager 2.2, freeware by John Rethorst. If it can be made to work with AppleScript, it provides yet another solution to the above.

Thus, the script is not limited just to the scriptable methods that the application provides; rather, the script now has many of the capabilities of a user sitting in front of a GUI. Granted, of course, that these GUI-like capabilities are not complete -- some output is readable but cannot be cut and pasted. Two caveats are applicable here:

But even taking these caveats into account, this is already enough to automate many manual tasks.

Moreover, building on and combining such capabilities, other tricks can be employed, so that in the end a script can do enough of the things that a user can, even if not in the same way. For instance, when a user can get information from just glancing at the screen, the script may have to manipulate the current contents of a window so that the information it is interested in is easily accessible -- for example, by deleting most of other extraneous information at the moment. Some practical examples are given in the next section. One cannot fully enumerate them all, but these examples hopefully give an idea of the thinking involved. This may seem more like kludging than "developing software", but if it works - it works.

See also:

The MacGyver approach

Since AppleScript is used to drive applications to perform routine tasks, it follows that applets can use some other applications to perform tasks needed for the given script’s goal. It’s only fair. From this follows a great strength of AppleScript -- it does not need extensive libraries. A number of routines is available to an AppleScript app in the form of other applications. Thus, for examle, Netscape can be used for retrieving Web pages, mail and USENET news, as well as files via FTP (for which Fetch can also be used) -- no need to write a specific set of routines to directly speak HTTP, FTP, NNTP, SMTP or POP3.

All of the above, and especially the incidental use of existing applications for various goals of a script give an impression of an AppleScript programmer as a sort of MacGyver. This, in fact, is a good approach to using AppleScript. No need to fret about the lack of some tools, or incurring unjustifiable costs (mostly in time) to build them. It is very probable that whatever you need is around; you just need to look differently at it. This is an even more efficient -- from the development time, but not necessarily the time-efficiency of resulting product, of course -- twist on the idea of code reuse.

Creative flow control

There are, obviously (see your AppleScript documentation) loops (or, rather the loop: repeat is the keyword used to create all sorts of loops: iterating over lists, looping while a condition is true or until a condition becomes true, etc.) and conditional branching.

The loop has a way to break out of it -- exit repeat statement. However, a continue (move on to the next iteration now) or its equivalent does not appear to exist. (continue is actually used to call "overridden" parent’s methods from a child script). The functionality of continue, however, may sometimes be effectively achieved by the following perversity, er, code:

repeat condition
  try
    ........
    ........
    error "MY_CONTINUE" -- This is our continue command     ........
  on error errTxt
    if errTxt ≠ "MY_CONTINUE" then
      error errTxt
    end if
  end try
end repeat

Now, of course, if there are try clauses which are nested inside our wrapping repeat and try clauses, we will need to handle them appropriately. In addition, not just the error message, but other information that error reports (error number, offending object, etc.), and not just the error message, needs to be propagated, if necessary.

Cumbersome, of course; but sometimes this is easier than restructuring the loop, using subroutines or even simple boolean flags to do this kind of flow control.

Extended character set

MacOS allows all characters from 8-bit extended ASCII character set in file names; extended characters also are valid for, and frequently occur in, identifiers or tokens in scripts and terminologies. It is important not to confuse these with similarly looking ordinary characters when reading the script (for example, confusing ellipsis with three periods, or guillemets with simple double quotes). The following table describes some of them.

Symbol…

Created with…

Uses

 

‘<Opt>’ +  ‘2’

Frequently included in filenames of third-party apps, e.g., Sequencher™ 4.1

‘<Opt>’ + ‘;’

Ellipsis. This is not three periods, this is one character. Used sometimes in menu items, and thus in strings (containing these names) which are passed as arguments to the application’s command to execute a menu item.

 

 

‘<Opt>’ + ‘<’, or

‘<Opt>’ + ‘ = ‘, or

‘<Opt>’ + ‘>’

Comparison operators, respectively "less than or equal to”, "not equal to” and "greater than or equal to”.

¬

‘<Opt>’+ ‘L’

‘<Opt>’+‘<Return>’

(The first one just inserts ¬, the second also inserts CR after ¬.

Line continuation character, indicating to AppleScript that next line continues the command started in the given line (or continued from before, of course). Otherwise, CR is the only statement separator/terminator.

«

»

‘<Opt>’ + ‘\’

‘<Opt>’ + ‘<Shift>’ + ‘\’

Called left and right "guillemets", or "chevrons", these symbols are used to enclose raw data (data that cannot be stored in any other AppleScript type). For example:

tell app "Satan” to «event getbehindme»

See also:

Memory and performance

Sometimes applications handling large amounts of data will run out of memory. While physical memory upgrade may be needed in general to speed up the system, this is not necessarily the solution. In fact, the first thing to be tried is to tell the application to request more memory for itself. To do so, select the application in Finder and press Opt-I. Select "Memory" from the drop-down list, and increase the memory available to this application.

In addition, extending memory of certain extensions (e.g. Application Switcher may speed up scripts. This is because most scripts often switch (see the use of activate command in AppleScript documentation) among (at the very least) the script itself, the application (or a few apps) that it is automating, the ubiquitous Finder.

Finder or other extensions , you may find that the Information window does not have a "Memory" option in the drop-down list. Here is a different way to achieve this result.

For Finder itself, it’s trickier still. Finder is always running, so it cannot be directly edited in the above manner. Instead, create a copy of it, move the original Finder from its place to backup, and put the modified one in its place (in System Folder).

Sometimes this fix will simply move the problem up to your script, which will now be running out of memory instead of the application it calls. Well, the same thing can be done to the script if it is saved as an application. If it is saved and run as a compiled script (instead of an applet), memory partition needs to be increased for Script Editor instead.

Finally, Bruce Perry notes in AppleScript in a Nutshell that often attached scripts run faster than stand-alone applets (page 7).

See also:

Scheduling

There are a number of applications that simulate the familiar flexible behavior of Unix’ cron:
For the latter, the executable for scheduling is Cron Setup. Alternatively, the crontab file itself may be edited. The syntax of this file mostly resembles its Unix predecessor (remember, it’s minutes first, hours second, the rest is documented; not hours first, minutes second as your intuition suggests).

Note: Running Cron Setup for the first time results in it inserting itself into the apps that are started on each MacOS startup; there is no need to put them into the "Startup" menu explicitly.

An applet can also be saved as "Save open", and its main loop be placed inside an idle handler (bracketed by on idle and end idle lines). The number that it returns is the number of seconds that will be passed from the end of one execution of the main loop to the beginning of the next one. A script may thus be made to execute repeatedly, say, every two hours; in the meantime it will not consume almost any resources.

Such a stay-open applet would do good to have an on quit handler, which should contain commands for cleaning up (removing temp files, closing applications, etc.) after the applet receives a quit event. Since including this quit handler overrides the default quit handler automatically inherited by the script, that handler must now be called explicitly by invoking continue quit.

Script Editor's limitations

Script Editor, the application for developing AppleScript scripts that comes with MacOS, imposes a 32K limit on a given script. This is where a big script may be broken up into smaller libraries, as above.

Script Editor, also, unfortunately does not provide much of the functions one would expect from a development tool -- neither decent text editing (even search or-- replace) nor debugging capabilities (like stepping or examining live data). We all know, of course, that printf() (er, alert and display dialog commands in this case) is well known to be the best debugging tool, of course, but...

Kitchen sink

AppleScript itself does not provide any functionality of a kitchen sink, although it may certainly automate scriptable kitchen sinks (perhaps, even this one?)

Java

With MacOS X, one is able to write native applications in Java instead of Objective C. But even prior to Mac OS X, Java can be productively used (albeit with the functionality of JDK 1.1.8) for writing scriptable applications, and little scriptable programs that are easier written in Java than AppleScript. It is possible that Java applications implementing some functionality are already in production on other platforms; they can be used on Mac OS and be made easily scriptable if needed.

See also:

Different versions of MacOS

It is important to keep in mind that things change from one MacOS version to the next, sometimes in a major way, that may certainly affect the performance of a script known to work robustly under a different version.

There is no need to mention exact differences among various MacOS versions here; but I wanted to highlight the importance of keeping that in mind. For a short summary of the differences, see Table 5.1 here. Make sure to find out other (significant and subtle) differences when running into problems using applications and/or scripts written for one version with another version of MacOS.

For an example of a serious problem with Finder on MacOS X, see this USENET thread.

Other random (but important) notes

MacOS has a limitation on size of a file or folder name - 31 characters maximum. Starting with MacOS 8.1, up to 255 characters are actually allowed in filenames, but only the first 31 characters are usable. (On MacOS 8.6, I have actually been unable to create a folder with name longer than 31 characters, but could easily create files with longer names.) Colon is not allowed as a character in a file name (it is used as a path separator.) The 31 limit is applicable only to individual file and folder names, the complete path does not have any restriction on its length.

See also:


Footnotes

    This particular enumeration signifies that this classification of modal dialogs is a pipe dream, rather than an idle thought; that is, I would consider it very useful to have this distinction promulgated.

    To quote a Bill Briggs column: "While it's true that you can now (as of OS 8.5), dismiss after a preset time dialogs that are put up by the "display dialog" command in the Standard Additions, that doesn't help you a bit with modal dialogs that are thrown up by other applications.”

    Though osaxen.com page states it’s free for non-commercial use, akua.com says that versions prior to 1.5 are freeware, period.

    Well, in fact MacOS 8.6 already features scripting addition that allows for URL access scripting. But you get the idea.

    In quotes because I am not sure the term is actually used with respect to AppleScript script objects, but effectively that’s what it is. A script can declare another as a parent, by setting property parent : <parentscript> at the top, and it will inherit properties and method from the parents.

    Application Switcher is an extension that allows the user to switch among active applications. Simple enough. Here is why we care. Even though AppleScript activate command is supposed to be bound to activate Apple Event, there are some occurrences that support a wacky supposition that scripts appear to send events to Application Switcher when an activate, for example, command is received for an application. I have doubts about this; the following, however, is evidence in support of this:

    See also this column.


References

Online

  1. AppleScript FAQ
  2. Columns by Bill Briggs
  3. Entering Script Information in Raw Format
  4. Automating Computer Tasks Through AppleScript, by Eugen Buehler
  5. The Source
  6. Mac OS 8 and 9 Developer Documentation
  7. The AppleScript sourcebook.
  8. Introduction to MRJ Scripting with AppleScript for Java.
  9. AppleScript overview
  10. AppleScript tutorial
  11. MacScripter
  12. Mac OS 8 Human Interface Guidelines
  13. Good description of data and resource forks: here, here and here
  14. How to prevent orphaned files
  15. Description of AppleScript Studio, a new addition in MacOS X with an unfortunate acronym
  16. A bunch of columns on Mac and AppleScript in the respectable Dr. Dobb’s Journal from the respectable (but acid-tongued) Michael Swaine
  17. Mac special characters tips
  18. About Apple Event Terminology Resources
  19. The Macintosh File System -- not a completely orthodox source (such as apple.com), but pretty much right on.
  20. Things Your Mother Didn’t Teach You by Bob Morrison
  21. Sample chapter from AppleScript in a Nutshell
  22. AppleScript to go
  23. Useful additions, extensions, consoles, tools and others...
    1. Scripting additions (aka osaxen) repository
    2. alt.language.applescript
    3. comp.sys.mac.*
    4. Akua Sweets - a large scripting additions package.
    5. Sandi's Additions
    6. Default Folder - used to store the default location into which a "Save as..." and similar dialogs open.
    7. Cron
    8. Java Runner

Books

  1. The Tao of AppleScript, 2nd edition. By Derrick Schneider et al. Published by BMUG

  2. AppleScript Language Guide, English dialect
  3. AppleScript in a Nutshell by Bruce W. Perry. Published by O’Reilly (I don't know, is this the "Sad Dog book"?)

© Gregory Golberg, 2003. All rights reserved.
Agree? Disagree? Would like more info? Just like to voice your opinion?
Comments are welcome





























































































































The blank space above is so the anchors to footnotes (and sections closer to the end of the article) work nicely. Nothing else to see here.