Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2005-645
$eventId
ID of event: wwdc2005
$eventContentId
ID of session without event part: 645
$eventShortId
Shortened ID of event: wwdc05
$year
Year of session: 2005
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC05 • Session 645

Building Automator Actions for System Administrators

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 may have transcription errors.

All right. 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, jeez, I forgot to close it. Automator happens to have some keynote actions, one of which is start the keynote slideshow. Okay, 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. Oh, thanks.

Very much, very much. 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 Pearl 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 gonna talk about AppleScript or Cocoa Stages, and I'm not gonna 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? Yeah.

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. So anyway.

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 a 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 gonna 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. 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 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'll always find an info.plist, 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, 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?

because it greps for things. 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. So... These actions you're gonna find are describing what application they belong to. Now the shell ones that we're gonna write don't really belong to an actual application. They're gonna 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 Ryan have anybody of 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, that a pancreas object would secrete enzyme objects into the 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. Give me a second.

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. 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 unicking 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, you could actually do that today in Automator. Let's do that today in Automator. Gotta start somewhere. We'll start with a 30-year-old shell script.

So let's see. A 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... 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 automated 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 could do -- is this giving anyone a headache?

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 gonna 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 gonna 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.

It 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 AppleScript and Cocoa stuff, with all due respect to the many fine AppleScript 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 in this role. It was, what is the Automator guy holding in the icon? He's holding a pipe, right? 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, and 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 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. 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 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. I clapped by mistake. This is six examples here. I know they're a little hard to see, but-- oh, jeez.

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, 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?

No, I'm not gonna like it better than Shell. Born Shell is the king of all programming languages. Born 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 "X-Cod." Is that right? X-Cod. X-Cod.

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'd 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 that, where you type a script like that, and away it goes, and 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 thing, other things should be called direction. And that's gonna 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 1, the direction is 0, 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 as environment variables. 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 $sortcolumn. And that's going to be a 1, a 2, a 3, a 4. 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 sortcolumn because of the binding. And then as the developer of this script, I can just use the $sortcolumn 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 into 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 getSelectedFinderItems. 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 AppleScript 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. I'm right. But he gave me this, I like this transition, this is neat.

So there's a whole lot of automated 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. And... Seriously, Sal, I love AppleScript. 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, and 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 things 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. And it has a main.nib in it, which is the user interface you're going to design. and has our favorite info.plist down at the bottom. Everybody love the info.plist? Favorite file in all of Mac OS X, the info, don't get anything, don't leave out any angle brackets in the info.plist.

but by now we're all really good at typing them up in VI. So we use Interface Builder to edit that nib file and build a little user interface. And we go through this binding process that I was describing before. You're gonna take this text field and I'm gonna attach it to a parameter called count. Then in my shell script, I'm gonna be able to refer to $count to know what you typed in there. So let's build an actual simple action here that sorts its input. We'll see if I can actually get a shell script going with a little bit of a GUI here. If we could go back to demo two again, please.

All right, just my spelling homework. Put that away, put that away. Eh, it was really nice, but no, I won't save that. So here we are in Xcode. We're gonna build a new... Project. Project? Project? What are we building here, a project or a pro-ject? Project. Project. Project. Get on with it.

All right, 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. Do you want to go forward or backwards and on what column?

What's the column delimiter? It'll be whitespace in this case. Although I may be too chicken to actually try sorting on column two, so we'll see how it goes. You know, the info.plist is certainly my favorite. And just this once, I think I'll actually edit it in here rather than in VI, but you all trust me that I normally do it in VI.

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 check boxes 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 8 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 gonna 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 gonna have two parameters, a Boolean, true or false, are we going backwards or forwards? And a number called column, that's gonna be our sort column. I'm gonna write my script in a minute, I'm gonna do a little UI work here. Here's the simple UI of an automated reaction.

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 in 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. Oops, 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 Audiosphere. 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. I've made two bindings. I'm going to save this and I'm going to go back to Xcode.

Oh, thank you very much. I don't know. Let's see. We'll look in the-- oh, here. 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.

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 Pearl or Ruby or something else if I wanted to use a different programming language. I'm going to use the Bourne shell right here. And by default it does cat. And for style principles, you really should set the path equals bin, user bin, S bin, user S bin. 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, 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 Born Show, we're saying if the backwards variable is 1, then we want to set something called direction to dash r. Then we're going to say sort dash k, dollar column, dollar direction. So that will wind up being something like sort dash k, 4, and then nothing if I didn't check the backwards box. Or it's going to say sort dash k, 4, dash r, if I did check the backwards box. 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. 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. NS button backwards uses a nonstandard 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 nib 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.

Oh, that's just fine. 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.

right down here, continuously updates value. You want to check that-- pardon me-- you want to check that for all of your-- text fields that you might build into an automated action this way. If you don't, it's not gonna build. AM Lint's gonna wake up and advise you of your rights. And, 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 AM Lint was going to complain about, it would say, "Your thing is too wide." So let's put -- your action is too wide. What did you think I said? I had to pause and take a water and collect my thoughts here for a moment.

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, 'cause 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 wanna sort these. on column one, but not backwards. So this is gonna run the command sort-k1, okay? Let me try this. Oh, there they are, sorted alphabetically. BC first alphabetically, but losers of the Grey Cup last year, as it turned out.

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 dash k two, right? If I click on the backwards box, it's going to go sort dash k two dash 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, 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... I'll 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, where should it start? Start at 1 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 select that, but I can't change it. It's not changing, right? But I can do this. So this is, I think, a little more bulletproof way to do it. I've got two objects bound to the same property, and that's completely fine. Bindings are going to keep them all in sync. And 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, I think, by end users once you actually give it to them. They'll be less likely, your dad will be less likely to phone you up because the automated 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 you wanted to know if it was because we were putting an Intel processor in it. And 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.

And you don't need to be a huge expert in it. So you're dragging things in, you're going to the bindings, and you're associating things with a model key path. 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 popup 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 gonna 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 the 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 that the default value for this box was false, for this parameter was false, or zero 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 noticed 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.

But sliders are the same -- come on, help me out here. I know we all want to go home, okay? I know we all want to get through this as quickly as we can. I love this snickering. 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 let you set how this slider is reacting. And then 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 is a matrix object. It's one object that has a couple of different things you could bind to. You will 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, the content objects, the content values, the selected index, 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, 0, 1, 2. 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 gonna 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, the 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.

A lot of options. Are you trying it right now, or do you know that? You just--no, thanks. Thank you, Mark. Mark Ritchie, your 1980--1996 World Web Objects Debugging Champion. Mark Ritchie, everyone. Pop-ups are the same kind of idea. They're a list--you know, there's a list of shirt sizes. I'm an extra medium myself. And you can ask for the-- 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 to selected index or selected value. Also new, 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 will fit in anywhere. It accepts anything. It just copies it to the next stage, and it prints out what it was. This is a, with the colons. And this is a great debug, this is, 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.

Let me show you something over here on this scriptability idea. Could I have demo two? Way ahead of me. Could I have demo one? And could I have the slides? Now could I have demo one? in demo two. Can we put this FireWire disk up on the screen? Is that possible? Why did they come over here? Oh, yeah.

I totally forget where I was. 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, two high, five four three two one, 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. Let's 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.

"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. Okay, okay. 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 dev console? 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. level. So that can be actually kind of handy. I put this kind of thing in my scripts all the time. Set-xv exec2 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. 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. Print env is also a useful command that you might want to put in. Let me just show you why here. Can 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. Can I have slides?

And can I have demo two? Thank you, thank you very much. I forget why I came over here. Let me just show you a couple of 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 gonna build a shell script automated interaction that does not really actually do anything. Does not do anything. Much.

Version one. I feel like putting parentheses in a file name is a bad idea somehow. All right, here we go. Let me just put this one in here, and I'm not even gonna build a UI for this one. I'm just gonna add main.command, and I'm gonna try this here, printenv, okay? That prints the value of all my environment variables and hands them to the next stage. We don't need that.

And let's say, you know what, I will do a little bit of a UI here. Let's do something real cheesy here. We'll take a UI with a pop-up button that I'm not even going to bother-- well, I'll make it small because they have to be small. There's a pop-up button, and here's another checkbox, let's say.

It has to be-- who knows what font that should be in? The small system font, right? And we'll do a couple of quick little bindings here. I want to show you this is a way to kind of debug your bindings. Back in info.plist, we're going to have-- And then we have the same default parameters. Here we go, indict.

Key foo, key string, 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. My cab here yet? Is he gonna remember to take out that last dictionary? Can he type the word dictionary? Here'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... foo to the selected value, which will be the-- a pop-up like this is going to say, item one, item two, item three. I want to bind foo to that. Okay, selected value. And this one here, I'm gonna bind the value of the checkbox, which is a one or a zero to bar. Okay? Simple?

So, and remember my script is just going to say print env. Print env 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 one. We're just waiting here. parseSelection is propertyList. parseFile is propertyList. 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 -- 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?.

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, whoop, hang on. Bring in a pop-up and make him small. Small size, and we're gonna bind him to, to bind his selected value to foo, and then I'm gonna 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-- Get that out. Anybody in the peanut gallery think this one might-- - - I was gonna. I like to put it on its own line, okay? Like that. Jeez. Thank you. 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. So here comes a script with a somewhat pointless UI. Does not do anything. 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 gonna be so great. You're all so kind for sitting through this here.

Item 2. So I'll run that there. I chose item 2, and you'll see in the output of printenv down here, somewhere it says foo equals item 2. Okay? That's-- Oh, come on. No, don't clap for that. 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. Check it here. And while you're at it, maybe 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. applications, utilities. Spotlight. 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 automate or create workflow. We'll 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, zoom. Here we go. 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 Automator scrolls better when you hide some of these things, and bring in a few of these other workflows that I've 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 workflow happens to run a big honkin' Perl script that parses things and gives you the first and lines, 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 could put in the sort thing and say remove the duplicates. That'll narrow it down a little bit.

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.

There are two good, oh, hey. Now, when I was first developing this, I was gonna 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 plug-in. 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?

Sit down. All right, that's it. I'd 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. Anybody 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?

All right, 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. laughter If you want to carry three and a half pounds worth of paper to Nova Scotia. Are there 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. Thank you.