The AppleScript programming language can be used to send send apple events to applications running under Mac OS X. These events can be used to programmatically control the applications, enabling you to do the GUI equivalent of chaining applications together like you would do for non-GUI applications with the shell.
AppleScript was only ever designed to be one language that can be used to send apple events - one with a particularly baroque syntax. Mac::Glue allows you to send the same events from Perl, with a similar (but less...well, confusing) syntax.
We're going to create a little Perl application that we can link from a Finder window that when executed will open a Terminal in the same location.
Before you can do anything with Mac::Glue you need to create a
binding for the application that you want to control. This enables
Perl to translate the commands you pass to the correct binary signals
to send. This can be done with the gluemac
command that's installed
when you install Mac::Glue
bash$ sudo gluemac /Applications/Utilities/Terminal.app What is the glue name? [Terminal]: This creates a file containing the binding:
/Library/Perl/5.8.1/Mac/Glue/glues/Terminal
It also creates a bunch of documentation in the same directory:
/Library/Perl/5.8.1/Mac/Glue/glues/Terminal.pod
This is the same documentation that's available from the Open
Dictionary
command in the Script Editor
application, except
(obviously) how to send the events from Perl rather from AppleScript.
You can read either, whichever makes the most sense to you. Translating
between to the two is relatively straight forward.
Once the binding is created we can create a new object:
# create a new object to access the terminal application my $terminal = Mac::Glue->new("Terminal");
We can then call commands on it:
# tell it to open a new window and run a shell command my $terminal->do_script("ls");
# tell it to come to the front above all the other # applications $terminal->activate;
In addition to issuing commands to the applications we can request the objects it's managing and properties these have. Let's look some code to work out where the finder is pointing at:
# create a finder object my $finder = Mac::Glue->new("Finder");
# create a command to get the url my $url = $finder->obj(window => 1) ->prop('target') ->prop('url');
# get the result my $path = $url->get;
This is roughly the same as the following AppleScript:
tell application "Finder" activate set path to URL of target of front window end tell
Let's look at the Perl code line by line. First we need to create a finder object that we can call methods on.
my $finder = Mac::Glue->new("Finder");
We now want to construct a command that we'll send across the wire later to request what we want to know. We grab the first window from the finder (which will be the top one, so the one that's showing) and then get the object that represents the directory that's showing in the window, and then get the path of that directory.
my $url_command = $finder->obj(window => 1) ->prop('target') ->prop('url');
We then send the command and get the result:
my $path = $url_command->get;
Let's build it all together into one script:
#!/usr/bin/perl
# turn on perl's safety features use strict; use warnings;
# load all the modules use String::ShellQuote; use Mac::Glue; use URI::Escape; use Data::Dumper;
# then open the terminal in the current dir terminal_in_dir(url_to_path(get_finder_url()));
We've got three functions wrapped together that is the basics of the script. The first function is the code from above that gets the current location of the top finder window:
sub get_finder_url { my $finder = Mac::Glue->new("Finder"); my $url_command = $finder->obj(window => 1) ->prop('target') ->prop('url'); return $url_command->get; }
The next function turns this url into a Unix style path:
sub url_to_path { my $string = shift;
die "Not a filename" unless $string =~ m{^file://localhost};
# remove leading scheme and host $string =~ s{^file://localhost}{};
# decode from a uri $string = uri_unescape($string);
return $string }
Finally we need to use the glue to talk to the Terminal application and tell it to open a terminal in the right directory:
sub terminal_in_dir { # work out what the path is my $path = shift; $path = shell_quote $path;
# open it in the terminal my $terminal = Mac::Glue->new("Terminal"); $terminal->do_script("cd $path; clear"); $terminal->activate; }
Converting a script into an application that we can simply double click in the Finder is actually quite complicated and requires quite a lot of files to be created.
Luckily, many Mac OS X applications - like Platypus - that can create .app Applications from a script. They pop up dialogs that ask you a few questions - the name of the script, what icon you want - and then create all the files needed from templates. Almost no thought needed.