DekGenius.com
[ Team LiB ] Previous Section Next Section

23.2 Osascript

Three command-line tools are provided for accessing AppleScript from Unix. The first, osalang, lists the scripting components present on your machine (see Section 4.2):

$ osalang -l
ascr appl cgxervdh  AppleScript
scpt appl cgxervdh  Generic Scripting System

If you have other OSA components installed, they will also appear. For example, if you use Script Debugger, you'll see AppleScript Debugger and JavaScript. These do in fact appear on my machine, but to reduce confusion, they are not shown here.

The two four-letter codes identifying each component are used by OSA programmers, but typically won't arise in the context of your AppleScript experience. Then comes a series of flags describing the capabilities of this scripting component (see the manpages for their meanings). Finally, we have the name of the component. The "Generic Scripting System" is the general front end to the OSA (what Chapter 4 calls the GSC); "AppleScript" is the AppleScript component in particular. You can use either of these two terms as a language specifier in calling the other two command-line tools, but their effect will be identical, since the GSC will treat AppleScript as the default component. In general, unless you are using other OSA scripting components, you'll have no need for osalang.

The osacompile command takes as argument a text file, or some text provided on the command line, and generates a compiled script file or applet. There are options letting you determine the characteristics of this file, such as its type and creator, but you typically won't need these. For example:

$ cat > textfile
display dialog "Hello, world!"
^D
$ osacompile -o compiledfile textfile

The result is a compiled script file compiledfile that opens into Script Editor when double-clicked in the Finder. We also generated an extra intermediate file, textfile. You can avoid this and type the script directly into osacompile:

$ osacompile -o compiledfile
display dialog "Hello, world!"
^D

The extension on the filename supplied in the -o parameter is used to determine the type of file that's created; this time we'll make an applet bundle:

$ osacompile -o appBundle.app
display dialog "Hello, world!"
^D

Further switches let you determine the applet's characteristics (see Section 24.1.1), so read the manpages.

The osascript command runs a compiled script file, a text file, or text provided from the command line. This command is the real key to bridging the gap between Unix and AppleScript from the Unix side. To enter text directly from the command line, you can use the -e switch, in which case the shell's usual quotational hoops will have to be jumped through; to enter the text of a multiple-line script from the command line in the Terminal, you can use the -e switch multiple times. For example:

$ osascript -e 'tell app "Finder"' -e 'activate' -e 
'display dialog "Hello, world!"' -e 'end'

But you can also type the script as a series of lines, as in the osacompile examples earlier:

$ osascript
tell app "Finder"
activate
display dialog "Hello, world!"
end
^D

In languages such Perl and Ruby, you can conveniently construct a multiple-line script as text on the fly by means of a "here document" and then hand this off to osascript for execution:

$s = <<DONE;
        tell app "Finder"
                activate
                display dialog "Hello, world!"
        end
DONE
`osascript -e '$s'`;

Again, if you don't have to construct the script on the fly, then consider not doing so. There is nothing wrong with preparing a compiled script file beforehand and just calling it with osascript. This is faster than handing text to osascript, because there's nothing to compile, and it saves you from all the quotational headaches.

Unfortunately there's no way to call osascript with parameters to be handed to the compiled script. Consider, for example, the following Perl script:

print "What would you like me to say?\n";
$prompt = <STDIN>;
chomp $prompt;
$s = <<DONE;
        tell app "Finder"
                activate
                display dialog "${prompt}!"
        end
DONE
`osascript -e '$s'`;

We can call that script from the command line, as follows:

$ perl hello.pl
What would you like me to say?
Howdy

This works fine; the dialog saying "Howdy!" appears in the Finder. Now let's think about how we would move the AppleScript part of this Perl script off to a compiled script file and call it with the value of $prompt as a parameter. We can't do this in any direct way. AppleScript has no ARGV mechanism for retrieving arguments from the command line. The only place for incoming parameters in a compiled script file is through the run handler; but this would require us to call run script, which we can only do by way of osascript, at which point the overhead is even greater. The only real alternative is to leave the data in a "drop," that is, in some location where the compiled script expects to find it.

One such "drop" would be an environment variable. (Recall from Section 20.5.3 that the system attribute command can read Unix environment variables.) We can't tell the compiled script the name of the variable, of course, so it must be hardcoded both into the Perl script and the AppleScript code. So, for example, the chain of command might run like this—from the Terminal:

$ perl hello.pl
What would you like me to say?
Hello, world

to Perl (hello.pl):

print "What would you like me to say?\n";
$prompt = <STDIN>;
chomp($ENV{whatToSayInTheDialog} = $prompt);
`osascript Desktop/helloAS.scpt`;

to AppleScript (helloAS.scpt):

set p to system attribute "whatToSayInTheDialog"
tell application "Finder"
        activate
        display dialog p
end tell

Another approach would be to use a file to transfer the data. Again, the file path will have to be hardcoded in both scripts. Here's Perl (hello.pl):

print "What would you like me to say?\n";
$prompt = <STDIN>;
chomp $prompt;
chdir(  );
open OUT, ">Desktop/dataForHello";
print OUT $prompt;
close OUT;
`osascript Desktop/helloAS.scpt`;

And here's AppleScript (helloAS.scpt):

set f to (path to desktop as string) & "dataForHello"
set fNum to open for access file f
set p to read fNum
tell application "Finder"
        activate
        display dialog p
end tell

That technique may appear extraordinarily elaborate, but if the AppleScript file were lengthy, the shoe would be on the other foot. To create and compile the text of the AppleScript code entirely on the fly, merely in order to embed a single value in it from Perl, would be overkill—and slow. To compile the AppleScript code beforehand saves a lot of time, and handing off a parameter to it from Perl by way of an environment variable or a file is a perfectly reasonable technique.

Do not use any scripting addition commands that put up a user interface, such as display dialog, directly from within osascript. The problem is that osascript is not an application context, so it has no provision for user interactivity. The dialog appears, but you can't click the buttons to dismiss it! The only escape is to kill the osascript process. The solution is to make sure that your script targets an actual application and calls any user interface scripting addition commands from within that. That's why we targeted the Finder in the preceding examples.

Using the -ss switch with osascript presents the results as AppleScript would present them:

$ osascript -ss -e '{"Mannie", {"Moe"}}'
{"Mannie", {"Moe"}}

If you omit it, you wind up with a flat comma-delimited list:

$ osascript -e '{"Mannie", {"Moe"}}'
Mannie, Moe

The fact that these are strings, that there is a list, and that one of the list items is itself a list, is completely lost from the representation of the result. Nevertheless, this representation has its place, especially when you plan to use further shell commands to process the result. In this example, I find all persons who appear more than once in my Address Book:[1]

[1] Thanks to Chris Nebel for this example.

$ osascript -e 'tell app "Address Book" to get name of every person' 
        | tr , "\n" | sort -bf -k2 | uniq -d
 Mark Anbinder
 Mary Byrd
 Jeff Carlson
        ...

The example takes advantage of the fact that a comma is the delimiter between names in the output from the AppleScript command. See Section 1.6, Section 2.7, and Chapter 4 for more examples of osacompile in action.

    [ Team LiB ] Previous Section Next Section