Enterprise IT • 1:06:49
Automator Workflows allow you to perform repetitive manual tasks quickly and efficiently. Learn how to extend this level of automation to system management tasks with step-by-step instructions on how to build Automator Actions with perl, the shell, and other scripting languages. This session will be of particular interest to system administrators and in-house developers who want to take advantage of Automator's inherent ease of use.
Speaker: Steve Hayman
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
I wanted to try something here. This is an Automator session and I've got a keynote presentation. So let's see what we can do here. Don't look at that one. Oh, geez, I forgot to close it. Automator happens to have some keynote actions, one of which is start the keynote slideshow. That'll tell Keynote to get going. That's another one. Stop the keynote slideshow. That's the one I chose. I didn't intend to choose that one.
I thought we'd be done quicker that way if I chose that one right off the bat. And I happen to have this little command line tool I wrote called Clapper Tool. And here comes a little experiment that may or may not work. No, no, no, hold your applause. You'll totally mess this up. I'm serious.
Automator already has a run shell script action in which you can type a shell script command of your choice. So I'm going to tell it to do that. The Clapper. All right, and we're going to run it. Just please, we're going to run this. Hang on, here we go. Ready? Everybody ready? Thank you.
Stop, no, you're gonna, hold it. We're on slide 39 already with all that clapping. So we're gonna talk about building actions for system administrators in terms of, by the way, where's the little clicker? Nobody gave me the little clicker. I'll have to keep coming back over. Thanks.
We're going to talk about using Automator with the shell and perl and other scripting languages like that. There is some new stuff that's just been added to Automator in the Xcode 2.1 build. Who's installed the 2.1 build? Who wondered what that was on the front list of packages about shell Automator Actions and so on? Well, you've come to the right place.
We're going to talk about using the run shell script action that I just used there where I typed a little fragment of shell script to cause it to run a tool. We're also going to talk, this is my favorite part though, building new actions with this thing called AM Shell Script Action. This is an interesting new way to build GUIs on top of shell scripts as part of the Automator Workflow. We're going to talk about mixing all this stuff together.
I'm not going to tell you every detail about the born shell and perl and ruby and other languages. I bought the ruby book at O'Reilly's over there earlier and briefly considered attempting to write a ruby script here during the demo. But frankly, I'm getting behind on reading my O'Reilly books. Every time I come to WWDC, I buy about 12 more.
And a year later, I've read about three of them. And then I come here and I think I've got this much space on my shelf at home. How many books have you got that can fit in eight and three-quarter inches, you know, with birds on the cover? I'd like a few more birds.
Oh, totally off track. And we're not going to talk about AppleScript or Cocoa Stages, and I'm not going to tell you every little detail of Interface Builder or Xcode, because there are 83 trillion little details of Interface Builder and Xcode, many of which you can ignore in this session.
So let me just do a quick review of Automator. Can we go back to demo two over here? Nobody uses demo one, do they? So here's Automator. And you're probably familiar with a lot of these actions that are in here already, but the neat thing that you can do with these new Automator shell actions is that you can build a whole new class of actions here where you get to design a user interface and interface builder and have a shell script execute in the background. Here's one I wrote.
A little while ago, we were having fun with this one the other day. This turns the system identifier light on an XServe on and off and will optionally flash out messages in Morse code. People think that the XServe doesn't have a display, but it really has a one-by-one yellow and off display. We were having great fun in the data center yesterday with this. All the machines in the rack were flashing their host name in Morse code. Kind of a useful system of min tool. If you can't remember what that one is, just watch it for a little while.
So this is actually running, this is a GUI that you design in Interface Builder. I'm going to show you all this stuff. And then it runs a command. It runs an actual Unix command. I have a tool called Identifier Light that flashes SOS on all the lights if you want.
And there are a variety of interesting ways you can build actions in this way. I wrote a little The Clapper action where I can say how many claps I want to do. Five claps, four, three, two, for instance. But my mic is actually on that computer so there's no need to try.
I'll be showing you a few more of these as we go along. There's one here that sorts items, for instance. This takes text from another action and runs the Unix sort command. And you get to decide in your UI that you build how you want the user to pick whether it's going forwards or backwards.
Or do you want to have a pop-up list to select numerically or alphabetically and so on. So this is, I think, a fun new frontier in building Automator Actions for those of us who still use VI to edit our Info.plist files. How many people edit an Info.plist with VI? Good. Go back to the slides for a minute.
Improved already, can you believe that this thing which has been out for three weeks has already been improved? Matter of fact it has, there is an improved run shell script action. Which is better than the old one. Now here's the minor problem. The new improved run shell script is not technically in the Xcode 2.1 build that you have. It is in the build that I have.
I didn't realize this until yesterday, until I built several dozen slides talking about the new run shell script action. The more exciting part is the AM shell script action, where you can build the graphical interfaces with interface builder. You'll get that in Xcode 2.1. You'll be getting the new run shell script action shortly, they tell me, in a software update, let's hope.
That's what the old one looked like. This is what the new one looks like. It's a little bit better. You can pick your shell of choice on a pop-up menu. It's got a number of, I don't want to say bug fixes, but shall we say improvements or enhancements in the way it can handle anything other than the most trivial line of shell code. A minor issue with the old one.
Coming real, real soon. I'm going to be showing you that one. I had the old one on here for my Clapper action just to prove to you that that one actually works. We run the new one over there. More excitingly is this thing called AM Shell Script Action, which is really a new project template in Xcode. If you start up Xcode 2.1, you're going to see something called a Shell Script Automator Action as one of the project types.
So let's talk about these actions in general and how the shell script action differs from the Apple script action or the Cocoa action that you may have seen already. The whole idea of Automator Workflows is to take a bunch of actions, string them together, you know, and there's a whole bunch that come with Automator.
There are these generic run-shell-script and run-Apple-script actions where you can type a little bit of code in a window like I did with that clapper thing and, you know, dispense with any fancy UI. Or you can create your own, and this is where this actually gets pretty interesting, I think.
A Cocoa Action, if you looked at the package, would look like that. You'd find, you'll always find an Info.plist, a nice XML file that describes what every action does, what kind of input it takes, what kind of output it produces. In a Cocoa Action, you'll find an executable file in a Mac OS folder, a piece of compiled code, and you'll find a user interface, a nib file from Interface Builder. In an AppleScript Action, you'll find much the same thing, except you'll have an actual script file rather than the compiled piece of code. They both have an Info.plist to specify parameters.
And they both have a nib file for specifying the interface. These new shell actions are the same general idea, except that there's always a file in there called main.command. You can't really change this name. It has to be main.command. But you can add whatever Perl code or shell code or awk or grep or t-rof or whatever you want in there to produce a graphical action.
Who enjoys talking like a caveman when they're talking about Unix command line things? Awk, grep, trough. Ask me why it's called awk. No, ask me why it's called grep. That's even better. Why is it called grep? I don't know why people find this to be difficult. I mean, this is the power of Macintosh and the simplicity of Unix all rolled in together here.
These actions you're going to find are describing what application they belong to. Now the shell ones that we're going to write don't really belong to an actual application. They're going to be marked as if they belong to terminal, but they're not really running terminal. But you'll see them in the terminal category. And actions also talk about types of input and types of output. And all that stuff is in the Info.plist file. And somewhere else is the actual executable code.
We script people. We love the ones that handle text. Take text as input, write text as output. Lots of certain Automator Actions produce text already, but there are plenty of others that don't. We get to write our own text actions here that talk to each other via standard input and standard output, if you're familiar with that idea from shell programming.
Perhaps this reminds you of Unix pipes. Does anyone have Unix pipes? Little blobs of useful functionality that you string together, the data flows through from the top down to the bottom. I've been learning recently, I was trying to think of a good example, and I've been quite interested recently in the pancreas. I've been learning quite a bit about the pancreas lately.
And I was considering constructing some kind of a workflow that sort of mimicked the digestive system, where you have a mouth and a throat object and a stomach, and a pancreas object would secrete enzyme objects into this small intestine object. And I decided not to do that particular demo because that particular workflow often reverses and goes the other way.
Automator is very much a one-way process, much like a traditional Unix script. This is the actual first Unix script I ever saw in London, Ontario, at a meeting of some group where Brian Kernighan, one of the fathers of Unix, came to speak, and he talked about software tools.
And the whole software tools idea was an approach where you wrote little tools that did one thing and one thing well. He got talking about all these odd tools like cat and tr and sort, and my first reaction was that, you know, what kind of an idiotic name for a command is cat? Short for catnate, which is apparently a word that means produce text. And he told us about all these little tools, and he types this little pipe on the flight, type, type, type. And I don't quite get what this is. The first one is displaying a file.
The second one, he assured me, was translating everything that wasn't a character to a new line. In effect, putting everything that wasn't alphabetic became a new line so that the words would be one on the line. The third one is making everything lowercase, and then we're sorting them, and then we're uniquing them to make sure there's no more than one adjacent occurrence of a word.
And then we're comparing them to the system dictionary. So what is this doing? Spell checking, right? It's a bunch of little tools that perform spell checking in this way. And I saw that example, and I thought, let's, you could actually do this. Let's do that today in Automator. Let's do that today in Automator. Got to start somewhere. We'll start with a 30-year-old shell script.
So let's see. Little tip for you. By the way, you can save interesting workflows down here in Automator and replay them on demand. You can actually double click here and individually put these items into your current workflow. Seriously, if you have actions that you've customized, your favorite collections of actions, put them into one big, hog, and pointless workflow, whether it makes any sense or not, and then keep that down here. You've now got your own private little library of my favorite 10 of the 150 Automator Actions.
So if we wanted to do spell checking in the Unix way, we might start out by getting the contents of a text edit document. I have a text edit document here. I'm not a very good speller, but I was reviewing the names of various Mac OS X releases. Text edit document.
And then we want to run a Unix script. We want to do the exact same thing that we saw in that pipe. We want to essentially convert all of the non-alphabetic characters to new lines, so all the words will be one on a line. And then we want to run another one.
We want to make all the words lowercase. This is running the TR program to make everything lowercase. Then we're going to sort them and we're going to unique them. And so you see I'm adding individual stages to my Automator workflow that really are being hooked up like pipes in the traditional Unix piping process.
Sorry? Zoom. Zoom, zoom, zoom. Here we go. So these are all pretty boring. I mean, they're a little -- I'm giving you -- I could do -- is this giving anyone a Are you loving the year of HD where we're using even smaller fonts than we ever did before just because we can? Isn't that exciting? How about I zoom out a little bit there? Each of these is one of these run shell script stages where I've typed in a little fragment of shell script and the output of one becomes the input of the next one.
Very much like that Unix pipeline. And the final one is perhaps the most useful Automator stage of the 150, view results. You're going to find yourself using this a lot. This is a stage you can drop in and it just says, here's what I got. Here's what I got from the previous stage. It will, if you like, pass it along to the next stage.
So it's a fine debugging tool to drop here and there in Automator. So I'm going to run this. Let's try running this. And hopefully we come up with a few misspelled words. Oh, there we go. A few misspelled words. The word and is apparently not in the system dictionary. That's most unfortunate. Huh? That's better.
was doing so well, boy. And we'll add View Results. Thank you, we're comparing it to the dictionary, then we're gonna View Results. I thought and was a correctly spelled word. I was really quite confident. Thank you. I can't spell it all. Now stop. No, I mean, you could do that on the command line. Why is this necessarily a big deal? Well, it's the same sort of idea as Unix piping. I'm going to let you in on a secret.
Automator was originally designed as a tool for stringing shell scripts together. All this Apple script and Cocoa stuff, with all due respect to the many fine Apple script and Cocoa programmers here in the room, was kind of an afterthought that was bolted onto this site. It's primarily a shell scripting tool. I think it's misunderstood. It was, what is the Automator guy holding in the icon? He's holding a pipe. He's holding a section of pipe.
It used to be called, anybody know what it used to be called? It used to be called Pipeline. That's right, it used to be called Pipeline. Then after that it was called Executor, briefly. And after that it was called, for a brief two-hour period, Apple Script Composer, I think, and that was short-lived. Then it got renamed to, I can't even remember what it is now, Automator. Last year at WWDC there had been all these changes.
Anybody who was talking about this tool, you could detect them pausing when they stated the name of the program because they had to think for a moment, what was it called now? And I have it on pretty good authority that the Automator team actually wrote an Automator Workflow to rename all the code in the project as the API kept changing, you know? Glad they haven't had to run that program this year.
So there's my little Unix pipe. But really, we can do better. This isn't a pipe I would expect to share, necessarily, with other people. I wouldn't want to call you and say, type in tr A to Z A to Z. No, no, no, capital A, capital Z. No, no, with a space in between them. I'm not going to expect people to type that in. But I happen to have pre-built a number of Automator actions that do many of these same things.
Like, for instance, in my terminal category over here, I can delete these two, the ones that put them one word to a line-- I'll take that one out-- and the one that makes them all lowercase. Because I happen to have written an action, one of these shell actions, called convert words of text.
And this is really running those exact same tools under the covers, except you get a little bit of a UI that was designed here in Interface Builder. I want to convert them to one word per line, remove punctuation. What kind of case do you want to make? I want to make them lowercase. So there's a better looking action that does very much the same thing.
In fact, if I run this workflow, since all these actions are producing text and emitting text, it still produces the same list of misspelled words at the bottom. I think you would agree. This one's a little easier to manipulate, and in particular, to share with other users than anything based on the shell script actually would be. I'm going to teach you how to design things like this so that you can wrap these shell commands with a nice graphical interface, give those to other people.
I can delete these other ones, too. This one sorts, and this one uniques them. I happen to have one here called sort items that you can say, on what column do you want to sort them on? Do you want to go forwards or backwards? Let's remove the duplicates.
So there we go, the same way. You build up over a while a collection of these little text processing actions that I think makes your life a lot easier. I think I've finally reached Automator nirvana in that I can write a workflow now without having to stop partway through and invent a new action. There was a long time there where I'd have something I wanted to do, and I'd get partway into it, and I'd think, gosh, this would be a great shell action in Automator. And I'd dash off into the tool and actually build things.
But once you get your own little library of these things together, you can start sharing them with other people, too. You can take each of these actions and put them in Library Automator, and other people can run them as well. If we could go back to the slides, please. So.
So how do these things actually work? Good question. This is the new run shell script action. Let me talk about the one where you just type a fragment of shell script for a moment before we discuss the ones where you actually build a GUI that talks to the shell script.
This is a simple action with a text field in it where you can type up a little bit of shell, a little bit of perl, a little bit of awk, whatever you like. Choose how you want the input to be handled as command line arguments or as standard input, and it will pass it on to the next Automator Action in the workflow. You have a choice of a whole bunch of interesting scripting languages on Mac OS X. In fact, this new version of run-shell script that you're going to get real soon now actually comes with a bunch of templates for different languages.
These are individual templates for a variety of different programming languages. Oh, boy. Hands off the build. Don't clap.
[Transcript missing]
I think I've blown it again. You know what? This is an automatic build, isn't it? Oh, I need a workflow here. I need a workflow to pause the, don't go to the next slide unless at least five seconds have elapsed.
Whew, there we go. These are examples in Automator of this new shell scripting. You can choose a scripting language on that little pop-up, and you can choose whether you want to handle input as standard input or as individual arguments, and it will cough up a little fragment in the appropriate programming language for you there. The most basic one in a shell, the one on the top left, the cat command, the good old cat command, all it does is copies its input to its output. Maybe a good one to try first. I wouldn't try selling that one necessarily.
But it gives you a start. Now, I enjoyed the Ruby ones at the bottom. I stared at these two Ruby examples for the longest time, not knowing a lot about Ruby. And I was this close to filing a bug report claiming that they were both the same, that we'd done something wrong.
We had the same thing for standard input and for command line arguments. It turns out there is an F in that one and a V in that one that I almost didn't quite see. Anyway. I enjoy Ruby. Are there any actual Ruby programmers here? It seems to have a lot of coolness about it. Is that true? Yeah? Am I gonna like it better than Python? Perl? Boren Shell and VI is the king of integrated development environments as well.
Oh, who's hissing? Are there Emacs programmers, users in here? VI, VI users? Who calls it VI? Who calls it SIX? I used to. We Canadians, we often pronounce things in interesting ways. For a long time we were saying "Project Builder" and that was freaking everybody out. We were saying "Java." "Java" and "Project Builder." I'd do sessions, they wouldn't know what the heck I was talking about.
So I've decided, and I'd like the Canadians in the room to back me up here to preserve this notion that we pronounce things differently in Canada, we call it "XCOD." Is that right? Anyway, there's two basic styles of writing one of these scripts. Do you want the input to your script to come on standard input, or do you want it to come as command line arguments? Experienced scripters are used to kind of flipping a coin in a lot of scenarios, which way they want to do it.
And Automator gives you templates to get you started in either case. You want to know which way you want to use. I think it's going to depend on the kind of action you're writing. If you're writing the sort of action that's going to process a whole body of text at once, and you've got some Unix commands in mind that are already reading from standard input and writing to standard output, like sort, for instance. That's the command that would sort backwards on column four.
That takes a body of text and sorts it and spits it out as another body of text. I do that with standard input. But if I wanted to process a list of each lines individually in some way, I'd probably do it as individual command line arguments. I'd click the other option on that thing, and they would be fed to my script.
As individual command line parameters that I could loop through in that way. I think it's going to be different in every case, so don't look for a blanket recommendation for me as to which is better. Those are the basic run shell script actions, but most of the ones I was showing you were little GUIs with Interface Builder. How is that actually working? Let's talk about that.
Well, with Run Shell Script, you've got a script where you type a script like that and away it goes. There's not really any user interface. With these shell script actions, you design a little nib file in Interface Builder, make it look really nice, you make it look really bulletproof so that people can't type words into fields that are only supposed to have numbers in them, for instance. And you arrange via the miracle of Cocoa Bindings to have the selections the user has chosen in the UI be reflected as environment variables for your shell script.
This is really pretty slick. So here's an example. This is a sorting items action. It's got some options. What column do you want to sort on? Do you want to go forwards or backwards? And we'll build something kind of like this in a few minutes. So how does this actually work? In Interface Builder, you set up what we call Cocoa Bindings that associate items in the user interface with a particular property that you want to, or I should say, a parameter you want to be available in the shell script.
So as an Interface Builder developer, I would be sketching out my UI like that. And in the Bindings Inspector in Interface Builder, I would say, you know what? I want whatever you type in that box to be bound to a parameter called sort column over here. I get to pick that name, sort column.
And I would go through that for all the various items in my UI. I would set up bindings that say that thing should be called remove duplicates. That other thing should be called direction. And that's going to be bound to either the word forwards or backwards, let's say. So you build a bunch of these bindings using Cocoa Bindings techniques.
And at the time that your script actually runs, Automator collects the values of everything in the user interface as a bunch of pairs. In this case, the sort column is one. The direction is zero because you can ask for the index of the selected pop-up rather than the word that's on the pop-up. That's sometimes easier. Anyway, Automator goes through and builds a bunch of these keys and values. What's in your UI? What parameters did we want to set? Set up some keys and values and hand them to the shell script.
And then when your shell script, remember it's always called main.command, when it runs, it's getting input from the previous stage, and it can refer to these environment variables in the born shell. You would just say dollar sign sort column. And that's going to be a one, a two, a three, a four.
It's going to be whatever the user typed in that box is going to be provided to the shell script as an environment variable called sort column because of the binding. And then as the developer of this script, I can just use the dollar sign sort column notation to retrieve the value of that particular environment variable.
The syntax for environment variables will be a little different in Perl. You've got an associative array called env, and you look things up by name. But every scripting language has some way to retrieve the value of an environment variable. And that's what you're getting from these shell script actions as they run. Then my thing sorts its input that it got from the previous stage, writes it to the output. And away we go.
It wouldn't be much fun if all we could deal with were text coming in. Text, well, it would be fun for me. But it wouldn't be much fun for many of the users of Mac OS X who are used to dealing with other things than just text. Automator has a lot of actions that talk about other things. They talk about pictures, and they talk about photos, they talk about files in the finder. We need a way to cope with that. Fortunately, Automator comes with 18 skillion conversion actions that can convert from different types to, eventually, a text representation.
They can convert from a file that's selected in the finder into the Unix path of that file, /volumes/steves/foo/bar, like that. Not the AppleScript way with the colons and everything, that I can't really, not my favorite way of referring to a path name, but they'll actually wind up giving you a proper Unix path because of these conversions that go from file system objects to text.
It's very common to have a workflow where you've got an item that's referring to files. There's one called Get Selected Finder Items. Already in Automator, that's asking the finder which items are selected. It's going to make a list of them and pass them to the next action. If I'm writing a shell action, I would prefer to get not some Apple script idea of record objects or something, but a list of the actual Unix path names. This will be accomplished magically by a conversion that's tossed in there in the middle by Automator for you. So there's a lot of interesting conversions in the system.
If you look in System Library Automator, you'll see a bunch of actions that end in .c action. These are all conversions that will secretly go from one data type to another. You can get from many... Here's the interesting thing. I drew this diagram. I actually wrote... Now call me a geek if you must.
But I actually wrote a shell script that crawled through all the conversions, looked at the Info.plist entries to decide what conversions went from what to what, and used OmniGraffle to build a big diagram of what I could convert. And then the real heroes of WWDC, if you ask any of the presenters, the real heroes are the people from a graphics company called Duarte Design who do a marvelous job of fixing up all of our diagrams. And you're about to see the diagram that the guy drew for me, and it made me weep with delight when I saw what he'd done with this diagram.
It was fabulous, except all the arrows were pointing in the wrong direction, and it was kind of making the exact opposite point that I actually wanted to make. But it was such a beautiful diagram when he was done with it that I thought I would change my talk to match the diagram. But he gave me this-- I like this transition.
So there's a whole lot of Automator Transitions out there. The ones in red and green and blue are the ones that can actually ultimately get converted to a text object, which is the kind that we want. So you can deal with iTunes tracks, because those represent files on the file system. For instance, you can deal with individual iPhotos in a shell action, because they're stored as a file somewhere. Eventually, the path name of that file will fall through this conversion system and wind up being presented to your shell-based action as a Unix path.
So let's talk about a couple of the existing actions that are in there. I showed you the Run Shell Script action already where you can type a fragment of shell script and away it goes. Another good one is Run AppleScript. I like AppleScript. I like it quite a lot. It's a mutant moon language from the planet Sedna, but I like it quite a lot.
Seriously Sal, I love Apple Scripts. Sal does a great job. Who doesn't love Sal? Give it up for Sal! You don't need to know AppleScript to Sal's level to get going with this particular action. There's a couple of very simple things you will find handy, even if you are primarily a shell programmer. AppleScript can do a lot in a short period of time.
This is a simple AppleScript action that just says, return some item of input. Give me a random item from something that came through before. But the sort of thing I think you might actually like a lot is that AppleScript has this thing called Do Shell Script with Administrator Privileges.
You as system admins are going to find this very useful. This is a way to run a shell script as root. An authentication panel will pop up, new in Tiger, the right kind of authentication panel will pop up, and it will run that shell script as root. So if you find yourself needing to build a workflow that's going to do something privileged, it's going to produce a bunch of files and you want to do something that requires root permission, you may find it handy to package what you need to do as a little Do Shell Script and then a string of shell code with Administrator Privileges, and that bit will run as root.
There's no direct way in Automator to run a shell action directly as root. Although I have a couple of ideas and I was passing them off to the Automator team and I said, what if we did it this way? What if we hacked sudo and we wrote something over here and I had it all working? They said, what are you crazy? That's evil. You should not encourage people to do that. So here's my politically correct stance. This is how you can get a shell script to run as root. And maybe there are other ways, but I can't tell you. Talk to me later.
All right, we want to build our own actions, though. It's 2005. I'm still using the same text editor I was using in 1981, but there has to be a new way to actually present the input to this shell script with interface builder. That's what I want to show you.
You start out in Xcode. You'll see the new shell script Automator Action. Isn't it great that Automator is the first thing that you see in Xcode when you're building a new project? You won't be tempted to go and write a device driver or anything like that. You'll want to get through all these things first before you proceed to the 40,000 other document types. We want to build shell script Automator Actions.
You're going to get a typical Xcode window like this that has a main.command in it. That's going to be your script that you're going to edit. It has a main.nib in it, which is the user interface you're going to design. It has our favorite Info.plist down at the bottom.
[Transcript missing]
All right, that's my spelling homework. Put that away, put that away. Eh, that was really nice, but no, I won't save that. So here we are in Xcode. We're going to build a new... Project. Project? Project? What are we building here? A project or a pro-ject? Project. Project? Project. Get on with it.
We want to build a shell script Automator Action. Here we go. I'm going to call it "Incredible Sorting Example of Doom." I hope there's not a limit in the length of the name of these things here. So, here in Xcode, Xcod, here in Xcod, I'm ready to go with my main.nib, my main.command, and my favorite, the Info.plist. And we're going to want to build something where the user can say what column do they want to sort on.
Unix has a sort command, and you can say sort-k4, and it means sort on the fourth column of the input. We want to have a checkbox that says do you want to sort backwards or forwards? That's sort-r. Sort has 53 trillion different options, and probably an Automator Action that encompassed all of those actions would scroll beyond the limits of what we have on our cinema displays full of checkboxes.
So we're going to keep this simple. A sword action, do you want to go forwards or backwards, and on what column? What's the column delimiter? It'll be white space in this case. Although I may be too chicken to actually try sorting on column two, so we'll see how it goes.
[Transcript missing]
If you're a maximum stud shell programmer, you're going to edit it here rather than use the new plist property editor that's built into Xcode, aren't you? You're going to be coming back to this rather than going to this wussy little thing here in Xcode where we can click on checkboxes and pop-ups to do what we want. We're not going to do that. We're going to go in here, and you absolutely must, as a developer here, get this part right.
You have a dictionary. It's empty here, as you can see, which is the default parameters of your script. You must define in the Info.plist every variable that you think you're going to set in the user interface that will be presented to the shell script. You have to type that properly in here as a key and a value, or it ain't going to work. : So here we go, let's see if I can type a syntactically correct plist on the fly.
I want to have a key called backwards. Do we want to go backwards or not? And I'll make that a, no, by default. You give a default value for these things. The default for sorting backwards is going to be false. And I want to have another key called column. What column do we want to sort on? A good default would probably be column one.
All right, all you human plist parsers, does that look okay? Oh, thank you. But you know what? We can always cheat. We can always say, where is it? File, parse file as property list. It's in here somewhere. Well, I'm sure. There we go. Parse file as property list. That's always good to check. Oh, it is good.
Worth checking that. If you mess this up, and I certainly did the first 800 or 900 times I did this, your action will appear to run properly. But since it doesn't actually load this thing, nothing else is going to work. So do take the time to verify that that Info.plist is right. If you must, you have my permission to go and use the little graphical pop-up editor here where we can see the parameters that are defined, column and backwards. If you want, you can use that.
So we've decided that we're going to have two parameters, a Boolean, true or false, are we going backwards or forwards, and a number called column that's going to be our sort column. I'm going to write my script in a minute. I'm going to do a little UI work here. Here's the simple UI of an Automator Action.
Well, let's just say that's a nice handy piece of text that we don't need, but it's something to start with a sort on column number... By the way, if you go Apple equal sign, it actually shrinks the thing in Interface Builder to fit exactly the text that you type.
Ooh, is that nice. We want to sort on what column? Well, we're going to bring a text field here. I'm going to type a column in here. I'm going to make it better in a minute, but for now it's going to be a text field. And we want to have a checkbox called backwards.
: That would be a basic UI. It's enough to get started. And using the amazing Cocoa binding system, you'll see that you have these inspectors here. I want to attach the value that the user types in that text field with the parameter called column that I've announced that my Automator Action is going to use.
Then my shell script, I can use the dollar sign column to pick that all up. Zoom? You're going to have a headache in a minute. You asked me to zoom. Oh, I move very slowly. Here we go. Oh, see, that's what happens when you zoom. Yeah, yeah, yeah, turn on Autosphere. Come up here if you have a problem. You stand here.
Yeah, what the heck, come on, let's go. So I've forgotten where, oh yeah, column. This is gonna be, whatever you typed in there is gonna be created as an environment variable called column when your shell script runs. And so the shell script can talk about $column and pick up this value.
Similarly, the backwards one here, I'm gonna bind it to another, I'm gonna bind its value, and check boxes have a value of a zero or a one, are they clicked or not? Same idea, backwards. Zero, it's not checked. One, it is checked. Backwards, okay, I've made two bindings. I'm gonna save this and I'm gonna go back to Xcode.
Oh, thank you very much. I don't know. Let's see. We'll look in the... Oh, all right. We'll actually use this. The parameters inspector. It appears to be backwards. What did I put? Am I binding? I put back... I was right. Thank you very much. If we run one minute over, we're going to know why now. We're going to have to stop and look at this.
All right, the incredible sorting example of Doom. Somewhere in here is a shell script. Oh, there's my shell script. Now, you can see that the first line says, what shell are we going to run? I could change this to perl or Ruby or something else if I wanted to use a different programming language. I'm going to use the born shell right here. And by default, it does cat.
And for style principles, you really should set the path equals bin, user bin, sbin, user sbin. This is old habits from a long time ago. Set the path properly. Make sure your script exits nicely. If your script doesn't exit with a zero status, the whole workflow is going to stop at that point. So that's a little bit of boilerplate I always like to put in.
And we've got this marvelous thing here called Demo Monkey where I can just go like this and it hauls in some code. I really want Malcolm Crawford, who wrote Demo Monkey, which is, Malcolm, are you in here? Love the Demo Monkey. Loving the Demo Monkey. Wishing that when I did that, it made a little fake typing sound so that people thought I was actually typing it in.
: They would think so until I picked up the water to try to drink it. So here in the Bourne show, we're saying if the backwards variable is 1, then we want to set something called direction to -r, then we're going to say sort -k $column $direction. So that will wind up being something like sort -k 4 and then nothing if I didn't check the backwards box, or it's going to say sort -k 4 -r if I did check the backwards box. Does that make sense? Well, let's see if it works, smart guy. Let's see here.
Build, run. You will find these things always wind up in that big terminal bucket when Automator actually launches. Two errors. Oh, isn't that interesting? Two errors. : How could there be two errors in something so simple? Well, you know what? There's the Automator style police are somehow involved here. NSButtonBackwards uses a non-standard font.
The text field does not continuously update the value. Well, actually, these are good things. We want all of our Automator Actions to look like other Automator Actions. And so in the build process, there's a little tool called AMLint that's running and being picky, picky, picky, and inspecting your nib and making sure you've built it according to the way that the Automator Police wants you to build Actions.
So I'm going to go back to my NIM file here, and I'm going to take that backwards button and bring up the font panel and say, yeah, yeah, yeah, we'll use the small system font. That better? And this one here is complaining, text field does not continuously update value. Well, this is actually good. In fact, I can see by looking at that that I actually typed C-O-L-U-M.
Who's used an application on the Mac where they type something in a text field that doesn't take until you hit return? Who hates that? Who wishes that all applications correctly updated values, even if you typed and you hit tab to get out of the field or you click somewhere else? That's what it's warning you about here. It wants you to arrange that this value is always updated no matter what the user types or whether they hit return or not. That's a simple checkbox over here. will be presenting his presentation on the Automator Action System.
and his team have been working on a new version of Automator Action for System Administrators. So we should be all right there. I've made a couple of changes. I said that should be the small font. This should update continuously. Let's try it now. I think that's right. Good. Now it launches Automator for you automatically. And I'm going to find down here in Terminal, hopefully, the incredible sorting example of Doom.
This is why we have the HD monitors here, because we have very long titles for our Automator. I'm surprised it's not complaining about the length of that title. You'd think, you know, of all the things A.M. Lint was going to complain about, it would say, your thing is too wide. So let's put, let's, uh, your action is too wide. What did you think I said? I'll have to pause and take a water and collect my thoughts here for a moment. Just excuse me. Okay.
I can't run this right by itself. It wouldn't really do very much useful. It would sort nothing and the output would vanish. But we're going to want to add, for starters, we're going to want the view results action here so we can see what our action actually did. And we're going to want to supply it with some text.
One way to supply things with text we saw before is to ask TextEdit for the contents of the front document. Another way, though, is you'll find an action here called Get Specified Text. And this is a place where you can just type in a little bit of text and away it goes.
So I have a handy-dandy list here because the season's about to start of all your Canadian Football League teams. Should we zoom in so we can take notes? Write those down. There's all your CFL teams. And we want to sort these.
[Transcript missing]
For the benefit of anyone from British Columbia, we can actually run it the other way here, and then BC will be at the top of the heap there and the bottom or something.
Anyway, we want to sort on column two by the team name. So as I'm doing this, it's actually changing the way it's running the sort command. So what command is it running here? Sort-k2, right? If I click on the backwards box, it's going to go sort-k2-r. Does that make sense? See, it's fairly straightforward to get things out of the UI here and bind them to a shell script.
It's still possible to mess this up, though. I mean, one could type, you know, something, sort on column two. That's probably not going to work properly, you know? Not really an option. Sort on column two, like that. That's probably not going to work either. We can make this UI a little more bulletproof than that, I think. I firmly believe that if you have a text field where someone can only type a number, you shouldn't let them type anything other than a number. We're passing silly options to the sort command right at the moment.
So we might go back to, stop Automator here, and go back to Interface Builder for a moment. Here's something I like to do for numeric input. I like to take a text field like that, and in the inspector, the main inspector, not the bindings inspector, but the main inspector, I like to take that text field and say, you know what, this one is not actually editable.
It's display only. It'll display a number, but that's it. And I like to bring in one of these stepper guys. This is a little object that can bump a numeric field up and down. And here's the trick, you bind it to the exact same thing that you bound the text field to.
So I'm going to bind that guy to, oh, here, by the time you've typed it once, it knows what the existing bindings are. I'm going to bind that thing to column as well. And you can in the inspector for one of these guys, start at one up to, I don't know, 100, something like that. So if we try this one now, this will be a little better, I think, from a UI perspective. Still the same script and everything. We'll run it again.
and in Terminal we're going to see the incredible sorting thing. And I can't, I can select that, but I can't change it. This is a little more bulletproof way to do it. I've got two objects bound to the same property. That's completely fine. Bindings are going to keep them all in sync. If you make a few small concessions like this to usability, you're going to have a sorting action that will be much more useful by end users.
Your dad will be less likely to phone you up because the Automator Workflow you built, he typed in the word two rather than the number two. My dad is buying an EMAC and he called me this week and it hadn't arrived yet and he wanted to know if it was because we were putting an Intel processor in it. I don't think so. Not yet. Let's go back to slides, please.
Oh, we built a script action. Isn't that marvelous? Now, there actually is a more elaborate version of this exact thing as a developer example that I believe ships with Xcode 2.1. It's the sort example that you see in my terminal collection already. It's got forwards and backwards and numerically, and you want to remove duplicates or not. So there's a more elaborate version of this exact same thing there.
Let's just review this binding thing. It is pretty important. You've got to get this right or it ain't going to work. I don't think it's that hard, but then I've been doing it for... I've been using Interface Builder since... When did Next release Interface Builder? Version 0.8? Probably not every... I'll bet there are people in here who have been using Interface Builder since Next Step 0.8, are there? Yeah, it's easy, isn't it? After you've been using it for 17 years, it's easy, right? But fortunately, I don't think you need to know everything about Interface Builder to build these shell UIs. What I showed you there is just about it. You lay out some text fields and you bind them, you kind of get the hang of this whole bindings thing.
You don't need to be a huge expert in it. You're dragging things in, you're going to the bindings, and you're associating things with a model keypath. You've got to get all these parts right, though. Everything has to agree. The binding name and the name of the variable in the shell script and the name of the thing in the Info.plist, they've all got to be the same, including case. You can't get the caps wrong in this scenario.
It's not going to work. : So also when you see this value parameter, if all you're doing is a simple shell script action with Automator, don't mess with the pop-up there. It should always say "parameters." You're binding what you typed here to the parameters object, which is this special Automator thing that's going to transfer those values to the shell script. It should also always say "selection," at least for a text field here.
So text fields are easy. We did a text field, right? The only little trick to remember is to click Continuously Updates Value, or AMLint is gonna complain. So don't forget to do those. Checkboxes, the same idea. You're binding again to selection, but this time rather than a string, you're getting a 0 or a 1. So your script can ask whether a particular environment variable is 0 or 1. That's the born-shell syntax for doing a numeric expression. Two parentheses, then the inside is evaluated numerically. So that says if isRadioactive is 1, then remove a file.
Now, because you're giving a default value to this thing, it's always going to be 0 or 1. You don't need to really check. Your Info.plist probably said that the default value for this box was false, for this parameter was false, or 0 or something. So you can avoid a certain amount of silly little defensive programming here. You don't really need to check that that variable is defined. If you've done it right, it will be defined. It will either have the default value or the value that the user chose, but it won't be empty.
Some of the other objects are a little more interesting. Sliders. We all did the text field and slider demo in our next career about a thousand times. We got quite to love these sliders. Although I notice they're not continuously sending actions anymore. When did we change that? Sliders used to be continuous. They're not continuous anymore. Or is it just me? Who has a continuous slider? It's not what you think.
Come on. Sliders are going to have a numeric value, 0, 1, 23.7. You've got a bunch of parameters in the regular interface builder inspector that lets you set how the slider is reacting. And so long as you bind it the right way, you're going to have a variable in your shell script that you can take a look at.
Radio buttons. This gets a little more interesting. This is actually a matrix object of, I don't know, NS button cells or something, but it's really one object that has a couple of different things you could bind to. You'll see in the inspector, in the bindings inspector for a matrix like this, you have a choice.
Do you want to bind to the content, or the content objects, the content values, or the selected index, or the selected object, selected tag? A couple of them are more useful than others. Selected index will give you the number of the thing that was selected, 01. 02. This might be a good idea if you were planning on localizing your application later.
Just in your script, deal with the number that the user chose rather than the string. But if you're lazy like me, you can just ask, bind to the selected value, and you're going to get an object that corresponds to the word that was actually shown on the pop-up.
Interface Builder, by the way, when you bring in radio buttons like this, you get two of them, and you hold down the Option key and you drag the corner and you get a bunch of them. If you ever wanted to know how to make a whole lot of radio buttons like that.
Or the Control key or something. I don't know. I have to do it four or five times myself to find the one that actually turns one. Which is it? Option? Command? Shift? Letter K? Anybody remember? I think it's Option, isn't it? It was on the slide, but that doesn't mean it's right.
[Transcript missing]
You can ask for the selected index, 0, 1, 2, or 3, or you can ask for the actual word that was on the pop-up. Your choice when you hook up the binding. You can bind either the selected index or selected value. also knew, by the way, completely changing gears for a moment, did you know that there was a user bin automator with Xcode 2.1? Thank you. Well, just hold your applause for a second until I tell you what it actually does. Or maybe we should stop here.
This runs workflows from the command line. Automator and then some workflow. And it runs the workflow from the command line. There are some limitations. I don't think user interaction can work. I don't think you can run this if nobody is logged in. I don't think you can do it as a login hook, which is what I really was hoping for. It's a good start. But it is a way that you could incorporate the running of an Automator workflow into a shell script if you want to flip things totally inside out.
I have an Automator workflow that actually builds dashboard widgets. I was trying to cut down on the number of demos I had to do. And I thought maybe I could then have a dashboard widget that used this to run an Automator workflow. And they could duke it out during the demo. And I could just go off and see. I'd have to try that. Maybe next year.
Let me give you a few random tips and then I'm going to show you a couple more interesting workflows and try to build something that solves an actual problem. It's not like me to get that far. A couple of tips here. There isn't really a debugger for shell scripts.
Perl has a debugger, but it would be a little weird to use it in this Automator context. But I want to encourage you to drop this view results thing in all over the place as you're developing a sequence of these actions. View results. It'll fit in anywhere. It accepts anything. It just copies it to the next stage and it prints out what it was. With the colons. And this is a great debugger. Of the available Automator debugging tools, this is one of the premier ones.
The other one, no. Another one is ask for confirmation is a way to get your script to stop at a certain point while you think. And if you haven't got like a clapper action on hand, this is a way to get it to stop and put up a little UI and wait for it. You can look around, did it create the file I thought it was going to? Did it actually tell iPhone to do something? So ask for results and wait for confirmation or ask for confirmation or two very useful examples here.
And Automator itself is actually Apple scriptable. The Automator engineering team has told me that this is scary. They've written self-modifying workflows that as they run, send messages to Automator to add and remove things on the fly from the current workflow. All you people who want Automator to do branching and looping, here you go.
There is, by the way, incidentally, there is an Automator Workflow that executes other Automator Workflows. You might have seen this. You can, I don't recommend this, you can make your Automator Workflow loop if you save it in a file and the last thing is execute this workflow again, this exact same workflow. It may be hard to stop it at this point.
But I actually, honestly, I did find that kind of handy. I put in a, there's a pause workflow action that waits for a certain number of seconds, then I had it run my workflow again. So it just sat there and ran for a while, creating dashboard widgets and opening them until my screen was actually full. You may find an actual use for this.
If you do, please drop me an email. But... Let me show you something over here on this scriptability idea. Could I have demo two? Oh, way ahead of me. Could I have demo one? And could I have the slides? Now could I have demo one? and Demo 2.
Can we put this FireWire disk up on the screen? Is that possible? Why did they come over here? Oh, yeah. You can rig these view results thing in on your own if you want, but if you had something like, what was I doing before? Sort on column O, and we'll bring in the text edit thing with get contents of text thing. There are scripts that you can, you can come up with scripts that do things like include view results in between every pair of actions in here. See if it actually works.
This is the first time I've tried it. Okay, well, theoretically it is possible to run scripts that will tell Automator, "Try Oh, okay, try it again. I have no faith that this will actually do anything different, but we'll try it again. Although I've been using computers long enough to know that just because it doesn't work in the first try doesn't mean it's not going to work in the second try.
Let's go back to slides for a moment. All right, how about we don't demo that? Here's another tip. You know what shell scripts and perl scripts and so on usually have two output streams. The standard output, which is what flows in Automator to the next stage, text usually, and the standard error, which is where interesting error messages will be discarded automatically by Automator so you can never see them. If you have something which is not working, you may find it handy to redirect the standard error stream somewhere other than the null device.
I find it handy to do this. See that there, exec2 into devconsole? That takes the standard error stream and redirects it to the console device, which means that any errors this script produces, I'm going to be able to watch them in the console application. I can fire up console and see error messages from my Automator app, if any. I can even do a command like the echo there to actually direct a statement in my shell script to the error device and then it goes to the console.
So that can be actually kind of handy. I put this kind of thing in my scripts all the time. Set-XV, Exec 2 into Dev Console. I was using the shell for, I swear, 15 years before I understood what these two lines actually did. Someone showed them to me in 1983 and said, here, trust me, someday you'll be presenting to a lot of people and you'll want to use this example. Set-XV in the Born shell turns on a form of shell debugging where it will echo every line as it's about to execute and it will take all the dollar sign things and expand them.
And if you do that and redirect it to the console device, you'll get a beautiful log of how your shell script is actually running, exactly what it's trying to do for every single line of shell script. This can be a marvelous debugging technique. Printenv is also a useful command that you might want to put in. Let me just show you why here. We have, uh, you didn't wait until I asked for it. I'm gonna have to do it again. Hang on. Wait for me. My show. I have slides.
And can I have demo two? Thank you. Thank you very much. I forget why I came over here. Oh yeah, let me just show you a couple little debugging things and then we'll try to assemble a potentially useful workflow. Let's see, let me build a new one here. I'm going to build a shell script Automator Action that does not really actually do anything. Does not do anything. Much.
[Transcript missing]
AM default parameters. Here we go, indict. Key foo, key string. "Nothing. String. Key. Bar. Key. Isn't this exciting? Isn't this the best part of WWC right here? What if my cab's here yet? That's what you're all thinking.
Is my cab here yet? Is he going to remember to take out that last dictionary that's sitting right-- Can he type the word 'dict'? Where's my spell checking workflow? Ugh!" So I just want to bind two things, right? I want to bind two things. And I got one called foo and I got one called bar. And I'm going to bind... and his team will be presenting the next session of Automator Action Sessions.
Okay, selected value. And this one here, I'm going to bind the value of the checkbox, which is a 1 or a 0 to bar. Okay? Simple? So, uh... And remember, my script is just gonna say printenv. Printenv is a Unix command that merely prints the value of all the current environment variables.
So if I've got a little script like this here, let's check the syntax of this while we're just waiting here. Parse selection is property list. Parse file is property list. Oh, phew, that's always nice. Oh, what did I forget? Command bin sh failed with exit code one. Aha, I always make, what is that? Couldn't read. What's wrong with it? No, it hasn't got that far yet. It's not actually, uh... It's running the build script. Put up the slides for a moment. Thank you.
In fact, you know what, let's go back to that movie. I love that movie on the first slide, you know? Steve, the name is too long. Get out. The name is too long? Oh, a trailing space. What kind of fool am I? Don't answer that. It's a rhetorical question. When I come back, I want to know what the purple thing with an S is. I don't know what that is. I've seen it go by four or five times already.
All right, you can come back over here. You can come back over to this one. Doesn't do anything much yet with no space. Think that'll be better? Thank you, everybody. Radar, where is Radar? Cannot create project whose name ends in space. How many people think, like me, that should be a priority one bug? Here we go, real fast. I'll just do one thing here. We'll get the pop-up. Whoops, hang on. Bring in a pop-up and make him small. is a small size and we're going to bind him to, to bind his selected value to foo, and then I'm going to go fix the Info.plist.
I'm going to fix the Info.plist. And do you think I would be able to cut and paste this by now or use the demo typing monkey, but...
[Transcript missing]
I like to put it on its own line, okay? Like that. Jeez. And back in our script, what were we doing? Printing NV or something pointless like that? All right, where are we here? What a great example, huh? Watch it fail again because it's still too long. Okay, this time it's working. Phew.
So here comes a script with a somewhat pointless UI. Does not do anything yet. Oh, it's got a pop-up. I forgot to take that up, but we don't care. We're all friends, right? I'm so glad we're having this rehearsal. You know, I love these WWDC rehearsals. Conference next week is going to be so great.
[Transcript missing]
But seriously, this is actually a fairly useful way to debug what's going on. If you've got a whole bunch of bindings and something went wrong and you can't figure out why, just check it here. And while you're at it, let's just pretend that this particular UI was-- there was more going on in this script. So I'm going to redirect printenv to devconsole.
And I'm going to start up the, yeah, you know what, just because I was waiting, all right? Maybe there was going to be more in that line. That's why that backslash is there. So we'll run that again and I'll go find the console app that I forgot to put in my doc. : Spotlight. I've heard about that.
Is that good? So console is constantly displaying everything that went to a certain output device. And we'll try Automator here with this modified-- oh, sorry, it's in terminal. Does not actually do anything yet. Run that now. And the output of that command should appear down here in the console.
Oh, there's the output of printenv in the console. That's a nice way-- If I'd known you were going to clap or something like that, I wouldn't have wasted so much time on the slides. Let me show you one last thing, then I'll take a couple of questions, because I can see we're almost out of time here. I decided that it might be useful to do an actual practical workflow. How many times has this happened to you? Here's my iPod. It's connected to this computer. Oh, it's in use. It could not be ejected. Please quit some random application and see if that helps.
Would it kill the finder to give you a hint about what application it was? Well, there happens to be a majestic Unix command called LSOF. And LSOF will give you a ton of data about every process and every file that it has open. And if you can manage to get through the incredibly long LSOF man page, you may discover one or two that can restrict it to a certain file system. So I had this idea that I'd write a workflow that would solve this problem for me. so Here's a trick. Control-click on anything in the finder and you can say Automator create workflow. It will make a new workflow that starts out with that thing.
That's kind of nice. So there's a workflow that starts out with that. We're going to add a plain ordinary shell script because I previously looked up the actual command that I want. Actually, this has to go to arguments. And we're going to delete that here. And we want to bring in a little bit of LSOF command that will actually tell me Yeah, zoom, zoom.
That's going to say -- see that dollar sign, ad sign? That says take the output of the previous command, which is going to be the path name that was selected in that first thing. And run LSOF with these mystery arguments on it and show me the results. Well, if we run that, oh, here's some output. Bash and Microsoft Word and there's a bunch of process IDs.
And I want to cut that down and make that a little more useful. So let me hide that and hide that, because the scroll is better when you hide some of these things. And bring in a few of these other work flows that I previously alluded to. See how we're getting a lot of stuff here? I've got this nice one called filter text by position.
And what I really want is line two through the end and the first field. This script, this particular work flow happens to run a big honking Perl script that parses things and gives you the first and lines and the last three lines or the fourth field or whatever you want.
So if we run that, we'll see that the result we're actually getting here, this is better. It's a list, one bash and Microsoft Word seems to have that device open twice. Well, we can put in the sort thing and say remove the duplicates. That will narrow it Now we're down to just, hopefully, Bash and Microsoft Word. And I'd like to present--this is a list, and I'd like to present it to the user.
Now, I don't know if there is a good Automator Action that presents a list of things to the user, but this is a fairly easy thing to do in AppleScript. I'm getting a list of strings, and I'd like to say, "Choose from list that list of strings with prompt. Here is the guilty process." : Now, when I was first developing this, I was going to kill the process immediately after that, but I thought that might be a little destructive.
So I think we'll leave it as this, but can you see how you can actually string some of these things together and come up with something that might be useful? This, if I were to give you this one, this wouldn't be that useful because it's always telling you about my iPod, which is maybe not that helpful.
I'm going to try something here that may or may not work. So far it has worked about two out of three times that I've tried it. I'm going to delete that thing that says "Start out with my iPod," and I'm going to save this in a special way.
I'm going to save it as a Finder plugin. Why can I not eject this freaking disk? That's now a Finder plugin. Automator lets you save things as plugins, which means you can now go over here and you can control-click on that. Automator. Why can I not eject this freaking disk? Thank you.
I'll be happy to take a few questions if there are any questions. I know people want to get out of here and get on their planes because we're all going to the football game in Halifax, Nova Scotia tomorrow. Have we got any questions? I think there's a few.
Are there anybody here from the Automator team that I might be able to pass these questions to if they get hard? Alright, I'd be happy to take a few questions. Yes, up in the front. One quick comment. Any actions you guys come up with, go to AutomatorActions.com and submit them.
Share them with the world. I know that's what you're all about, open source and all that. So log on and put them on there, please. Are there any good books about Apple script programming in general, Hanan Rosenthal? Yeah, let's... Oh, he's got one. What do you know? How shameless of you. Have you got one for me? That would be a nice turnaround if you had a book for me.
If you want to carry three and a half pounds worth of paper to Nova Scotia. Any other questions? I'm hanging around for a bit. If anybody's got any questions, I'd be happy to take them up front. I thank you all very much for staying to the end of WWC, and I hope you enjoyed the show. Thank you very much. Thank you.