Keywords: AppleScript, scripting, automation, application glue, Mac, MacOS
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...
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:
Obviously, modal dialogs are an important issue to consider when scripting an application.
See also:
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:
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.
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:
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.
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:
See also:
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:
Which brings us to another way:
set (x1, y1, x2, y2) to reposition window 3
reposition window 3 to (0, 40, 800, 840)
See the Akua Sweets documentation for details. You may need to experiment to find out the needed window id to be scripted.)
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:
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.
The loop has a way to break out of it --
Now, of course, if there are
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.
Creative flow control
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.
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
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.
Extended character set
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:
|
See also:
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:
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, 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
See also:
printf()
(er,
alert
and display dialog
commands in this
case)
is well known to be the best debugging tool, of course, but...
Kitchen sink
Java
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.
See also:
Other random (but important) notes
Footnotes
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.
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.