|
|
|
|
AppleScript in a NutshellBy Bruce W. PerryJune 2001 ISBN 1-56592-841-5 526 pages, |
Chapter 7
Flow-Control StatementsThe flow-control statements in AppleScript orchestrate the "flow" or the order in which the code statements execute in your scripts. Programmers will be familiar with AppleScript's
ifconditional statements, which are very similar to the syntax of Visual Basic, Perl, and other languages. These statements execute code only if the tested conditions aretrue. AppleScript handles loops in script code with several variations of therepeatstatement, similar to the "for," "foreach," or "for each" statements in other languages. Therepeatflow-control construct repeats the execution of code a specified number of times or for each member of a container, such as alisttype. Or, it repeats a code phrase a specified number of times:repeat 100 times...end repeatYou will be pleased to know that AppleScript has more than adequate error-trapping capabilities. This is accomplished by enclosing the statements that may raise errors in a
try...end trystatement block. In addition, you have already seen dozens of examples of thetell..end tellstatement in earlier chapters. These statements specify the objects, usually application objects, that receive the commands or Apple events that your script sends. You specify the targets of different script commands by using thesetellstatements.You can nest flow-control statements within other flow-control statements. Most of these statements end, appropriately, with the reserved word
end, optionally followed by the statement identifier, such astellorrepeat. An example is:tell app "Photoshop 5.5"...end tellThe
ifandtellstatements allow "simple" rather than "compound" usage, such as:if (current date) > date "1/1/2001" then display dialog "Welcome to 2001"These simple statements appear on one line, they do not contain other code statements, and they do not need to be completed with the
endreserved word. This code shows some nested flow-control statements and simple statements:tell application "Finder"set freeMemoryBlock to largest free block(* Here's a simple statement; no 'end' is necessary *)if freeMemoryBlock < 10000000 then display dialog ¬"Memory is getting low"set listOfProcesses to name of processesif "BBEdit 5.0" is not in listOfProcesses then (* compound 'if'statement *)tell application "BBEdit 5.0" to run -- simple 'tell' statementend ifend tellSuffice it to say, flow-control statements are how AppleScript derives much of its power and complexity. You will develop very few scripts that do not use at least one flow-control statement. Table 7-1 lists the statements that this chapter describes.
Table 7-1: Flow-Control Statements considering
repeat with loop variable
continue
repeat integer times
error
return
exit
tell simple statement
if simple statement
tell compound statement
if compound statement
try
ignoring
using terms from
repeat
with timeout
repeat until
with transaction
repeat while
considering [but ignoring] end [considering]
Syntax
Considering case"animal" is equal to "AniMal" -- returns falseend consideringDescription
Use
consideringstatements to specify the elements that should be considered during string comparisons and communications with other applications. The statements that constitute the comparison are enclosed in theconsidering...endconsideringblock. This statement block affects how each of its enclosed statements is processed. Theconsideringstatement can also alter AppleScript's default behavior for the code that is executed prior to the end of theconsideringstatement (signaled by anendorend consideringphrase). For example, if you wanted to compare two strings and take upper- or lowercase characters into account, but ignore any white space in the strings, then you would use the statement:considering case but ignoring white space...endconsidering. AppleScript's default behavior is to consider elements such as case, white space, and punctuation when it compares strings for equality. The following constants can also be used in the considering statement (Chapter 6, Variables and Constants, discusses AppleScript's constants):
application responses
case
diacriticals
expansion
hyphens
punctuation(i.e.,. , ? : ; ! \ ' " `)
white space
AppleScript considers by default an application's responses to any Apple events that your script sends them. You can use the
ignoringstatement to ignore responses from an application, as in considering case but ignoring application responses.There are a few instances when ignoring application responses might make sense, such as when you are sending quit commands to several running processes. If one of the processes responds to the command with an error, then the script ignores its response (as well as any other application response) and thus prevents it from disrupting the execution of the rest of the script. See "ignoring" in this chapter for more details.
Examples
This code shows how to use this statement with a fairly complex string comparison:
tell application "Finder"considering case but ignoring punctuation, white space and hyphensset theTruth to ("voracious appetite" is equal to "voracious, ¬appetite") --returns trueend consideringend tellThe example tells AppleScript which elements to consider and ignore when executing the string comparison within the
consideringstatement block. Since white space, hyphens, and punctuation should be ignored in the comparison, the two strings turn out the same. Therefore, thetheTruthvariable is set totrue. If you are wondering why you would ever ignore these elements in a string comparison, programs often deal with a lot of junk characters and tokens, such as markup-language elements, which are returned from applications or web pages. Theconsideringstatement allows you, in a minimal way, to filter out elements that you do not want to include in string comparisons (unfortunately, you will have to write custom functions or use an HTML-parsing osax to filter out the common < > characters in hypertext markup language [HTML], as AppleScript does not consider them to be "punctuation").continue
Syntax
On parMethod(int )If somethingTrue then(* code statements here *)elsecontinue parMethod(int) -- call parent script's parMethod versionend ifend parMethodDescription
The
continuestatement is used to call a parent script's method from a child script. In AppleScript, a child script can inherit properties and methods from a parent script. This topic is covered in Chapter 9, Script Objects and Libraries. The child script specifies its parent (if it has one) by declaring a parent property at the top of the script:property parent : NameOfScriptThe NameOfScript part can be either the name of a script object or an application, such as:
application "Finder"A child script inherits the methods of a parent; it does not have to define these methods. However, the child script can "override" the parent method(s) by redefining them in the body of the script. Within these redefined methods, it can use the
continuestatement to call the parent method. The following example constitutes a child script that calls its parent's version of the parMethod function based on the magnitude of the numerical argument passed to the method. The child object handlesrealnumbers and the parent handles integers.Examples
On parMethod(int )If (class of int is real) thenReturn (int * 2)Elsecontinue parMethod(int) (* it's an integer so call the parentparMethod *)end ifend parMethoderror
Syntax
error myErrText number 9000 -- myErrText contains the error descriptionDescription
The
errorstatement allows you to raise an error based on certain script conditions. Why would you ever want to raise an error? You may want to catch an error in a function but handle the error in the part of the code that called the function, higher up in the call chain. So you would use theerrorstatement to pass the error up to the calling function, such as in the following example. This is similar to "throwing" an exception in Java.Examples
This example uses a getNumber method to get a number from the user, but it does not bother (for the sake of demonstration) to check the entry to ensure that the user has entered a valid number. If the user enters data that is not a number then the statement:
return (theNum as number)causes an error, because AppleScript cannot coerce non-numeric characters to a
number. To be specific, this is AppleScript error number -1700. The getNumber method catches the error and uses anerrorstatement to pass the original error's error message and number back to the calling handler (in this case the script's run handler), which then catches the "re-raised" error and displays a message:on runtrydisplay dialog "Your number is: " & (getNumber( ) as text)on error errmesg number errndisplay dialog errmesg & return & return & "error number: " & ¬(errn as text)end tryend runon getNumber( )set theNum to (text returned of (display dialog ¬"Please enter a number:" default answer ""))tryreturn (theNum as number)on error errmesg number errnumbererror errmesg number errnumberend tryend getNumberThe
errorstatement also gives the scripter more control on how program errors are handled. For example, you can catch an error in a script with atryblock (see "try" later in this chapter), examine the nature of the error, then re-raise the error with theerrorstatement providing a more lucid error message to the user. This is best illustrated with the following code, which catches an error caused by coercing a non-numeric string to arealdata type:(* use the display-dialog scripting addition to ask the user to enter a number *)set aNum to the text returned of (display dialog "Enter a number" ¬default answer "")tryset aNum to aNum as real (* non-numeric string like "10ab" will raise an error *)on error number errNumberset myErrText to "Can't coerce the supplied text to a real: " & ¬return & "The AS error number is " & errNumbererror myErrText number 9000 -- add your own error numberend tryThe code first asks the user to enter a number, using the display dialog scripting addition. This produces a dialog box with a text-entry field. If the user enters some text that cannot be coerced to a
number, such as10ab(the included lettersabcause the coercion to fail), the expression:set aNum to aNum as realcauses the script to raise an error. The
tryblock catches the error, and then processes the statements following theon errorcode. These statements include:error myErrText number 9000which produces an AppleScript error-dialog box and adds the scripter's custom message (stored in the variable
myErrText). It also provides a custom error number of 9000. You can create your own groups of error numbers or variables for certain error conditions, which your script can then identify and respond to with more accuracy and clarity than if the scripter only relied on AppleScript's error numbers.The next two examples illustrate the setup and usage of custom error variables. The first example is a script that contains several user-defined error variables for some common errors that occur in AppleScripts. This script is loaded into the current script using the load script scripting addition (Appendix A, Standard Scripting Additions, discusses scripting additions, or osaxen). The example only contains three constants, but it could define dozens of them to accommodate most or all of the possible script errors that could occur. The constants are set to the actual values that AppleScript assigns to the errors that represent, for example, the failure to coerce data from one type to another (i.e., error number -1700):
set FAILED_COERCION to -1700set MISSING_PARAMETER to -1701set TIMEDOUT_APPLEEVENT to -1712You can then test for certain errors and, if you discover them, display more informative messages or take some other appropriate action. For example, the script in the following code sets the variable
objErrorsto the script object defined in the prior example. It then uses theFAILED_COERCIONandTIMEDOUT_APPLEEVENTconstants from that object to test for these error conditions. In other words, theTIMEDOUT_APPLEEVENTvariable contains AppleScript's actual error number for Apple events that time out (-1712), but it is easier to remember if it is stored in a variable with a coherent name. If either of these errors is detected, the error statement is used to produce a dialog box with your own error message:set objErrors to (load script file "HFSA2gig:nutshell book:demo ¬ scripts:scr_0504")(* this script object contains
the user-defined error variables *)set userReply to the text returned of (display dialog ¬"Please enter a number" default answer "")tryset aNum to userReply as real (* if the user doesn't provide a numberthis statement will fail and the try block will catch the error *)on error errM number errNumberif errNumber is equal to (objErrors's FAILED_COERCION) then(* FAILED_COERCION is a property of the script object stored in objError *)error "The number you provided was not a valid integer or real."else if errNumber is equal to (objErrors's TIMEDOUT_APPLEEVENT) thenerror "For some reason AppleScript timed out."else -- default error message for all other errorsset defMessage to "Sorry, AppleScript error number: " & errNumber & ¬ "occurred in the script. Here's
AppleScript's error description: " & errM error defMessageend ifend tryThe
errorstatement includes a number of optional parameters. It is important to remember that you supply the values for these parameters (if you want to use them). Withtryblocks andon errorstatements, AppleScript itself will fill these parameters with values (see "try" later in this chapter):
- A non-labeled parameter that contains the text describing the error, as in
error "The Apple event timed out".
- A labeled parameter that identifies the error number, such as
error "TheApple event timed out" number 9000. You use the keywordnumberfollowed by theinteger. If you do not include an error number, AppleScript gives this parameter a default value of -2700.
- A labeled parameter that identifies the object that caused the error, as in
error "The Apple event timed out" number 9000 from userReply. You use the keywordfromfollowed by a reference to the object, in this case the variable that caused the coercion error.
- A labeled parameter that involves the reserved words
partial resultfollowed by a value of typelist. If the command that caused the error involved the receipt of return values from multiple objects (e.g., a command sent to several database files to get some data), then thelistvalue contains any of the values that were successfully received before the error halted the operation. AppleScript gives this parameter a default value of the empty list ({ }).
- A
tokeyword or label followed by a word that identifies a class type, such asboolean,string, orinteger. If the command that caused the error received the wrong type of parameter value, then thetolabeled parameter will identify the correct data type that the parameter expected.
The following example demonstrates how to pass the information about an AppleScript error to an
errorstatement. The script intentionally raises an error by using astringinstead of abooleanexpression in anifstatement. Then it passes the error data as a longstringto anerrorstatement:tryif "not a boolean" then (* this causes an error, caught in the try block *)beep 2end ifon error errMessage number errNum from errSource partial result ¬errList to class_constant -- various variables store information about the errorset bigmessage to "The error is: " & errMessage & return & ¬"The number is: " & errNum & return & "The source is: " & errSourceerror bigmessage -- error statement displays dialog box to userend tryexit [repeat]
Syntax
repeat 2 timesexit repeatend repeatDescription
The
exitstatement causes the flow of script execution to leave theexitstatement'srepeatloop. The execution then resumes with the script code following therepeatloop.exitcan only be used inside of arepeatloop, regardless of therepeat-loop variation. Usingexitis the conventional way to exit arepeatloop that has no conditional statement associated with it, as shown in this example. In other words, this form ofrepeatis an infinite loop:repeatset userReply to the text returned of (display dialog "Want to get ¬out of this endless loop?" default answer "")if userReply is "yes" then exit repeatend repeatThis endless loop can also be exited by clicking the Cancel button on the dialog produced by display dialog, which terminates the execution of the script.
if simple statement
Syntax
If theBool is true then exit repeatDescription
AppleScript supports the simple
ifstatement that is similar to Perl's. You can use a statement such as the following:if (current date) is greater than or equal to date "1/1/2001" then ¬ display dialog "Welcome to 2001"You do not have to "close" this statement with an
endorend if, as you do with more wordy compound statements. You just include the reserved wordiffollowed by abooleanexpression (returnstrueorfalse), the reserved wordthen, and whichever statement you would like to execute if thebooleanexpression returnstrue. This example has several different versions of theifstatement. Since there are two different versions of the same date-test expression, this script will create two of the same dialog boxes:if (current date) is greater than or equal to date "Saturday, January ¬1, 2000 12:00:00 AM" then display dialog "Welcome to 2000" (* simple if statement *)if (current date) is less than date "Saturday, January 1, 2000 12:00:00 ¬ AM" then display dialog "Enjoy end of
1999" -- simple if statementset yearCount to 0if (current date) 3 date "Saturday, January 1, 2000 12:00:00 AM" then--compound if statementdisplay dialog "Welcome to 2000"set yearCount to yearCount + 1else if (current date) ¬< date "Saturday, January 1, 2000 12:00:00 AM" thendisplay dialog "Enjoy end of 1999"end ifUse a simple
ifstatement if the script only has to execute one line of code in the event that thebooleanexpression teststrue. Otherwise use a compoundifstatement.if [then] [else if] [else] end [if]
Syntax
If theBoolean then(* code statements *)else if anotherBool then(* code statements *)else(* code statements *)end ifDescription
The compound
ifstatement can be used to test severalbooleanexpressions and only execute subsequent script code if the enclosing expression istrue. The syntax of theifcompound statement is almost exactly the same as Visual Basic's, JavaScript's, and Perl's, with some minor differences (for example, VB pushes the else and if together to make "elseif"). A plain-English pseudocode translation of this statement would be, "if this happens then run this code; else if that happens then run this code; else (if neither of the first two things happen) then run this default code." You do not have to include any curly-brace characters ({ }) to enclose the conditional script code that theifstatement contains. As long as you place different lines of code on separate lines, then thethenpart ofif...thenand theifpart ofend ifare optional, as in the following code. The compiler puts the "thens" and "end if" in the right places.Examples
In the following example, the
(current date)is the testeddate "1/1/2001"
booleanexpression. If it'sfalse, then theelsestatement(s) will execute.Set yearCount to 0if (current date)date "1/1/2001" -- compiler will fill in 'then'
display dialog "Welcome to 2001"set yearCount to yearCount + 1elsedisplay dialog "Enjoy the end of 2000"end -- 'if' is optionalignoring [but considering] end [ignoring]
Syntax
ignoring application responsesend ignoringDescription
You can use this statement block to control
stringcomparisons. Theignoringstatement is used withapplication responsesto disregard any responses from the apps that receive the script commands:ignoring application responses...end ignoringThe following AppleScript constants are the parameters to the
ignoringstatement. They are all considered by default:
application response
case, as in upper- or lowercase
diacritical, like the two dots in ü.
expansion; if ignored, then æ, Æ , ?, and ? are equal to ae, AE, oe, and OE, respectively. These letters are by default not equal to each other.
hyphen, as in "-"
punctuation; these marks are ignored by the statement ignoring punctuation:. , ? : ; ! \ ' " `
white space
Examples
This code shows how to use
ignoring...endignoring. Believe it or not, the code:"j'u-n,k t?'ext" is equal to "junk text"returns
true, because the enclosingignoringblock tells AppleScript to ignore punctuation and hyphens when making the string comparison:ignoring punctuation and hyphens but considering casereturn ("j'u-n,k t?'ext" is equal to "junk text") (*returns true because punctuation is ignored in the comparison *)end ignoringIf you want to ignore more than one constant, just separate them with the
andoperator:ignoring punctuation and white space and hyphens and expansion.You can also use the
butconsideringparameter followed by one of the specified constants (e.g.,hyphen,whitespace) to ignore some elements but consider others:ignoring punctuation but considering white spacerepeat end [repeat]
Syntax
repeatif someTrueCondition then exit repeatend repeatDescription
repeatwithout any conditional statements associated with it results in an infinite loop. In most cases, you need to use anexitstatement to terminate the loop and resume execution with the statement that followsend repeat. This statement begins withrepeaton its own line and finishes with anendor anend repeat(therepeatpart ofend repeatis optional). All of the statements that should execute within the loop appear between therepeatandend repeatlines. You can nestrepeatloops within each other.Examples
This AppleScript shows one
repeatloop nested within another. It also illustrates that theexitstatement only exits therepeatstatement in whichexitis contained. So the example actually needs twoexitstatements to emerge from its repetition purgatory:repeat -- outer repeat looprepeat -- beginning of inner repeat loopset userReply to the text returned of ¬(display dialog ¬"Want to get out of inner endless loop?" default answer "")if userReply is "yes" then exit repeatend repeatset userReply to the text returned of ¬(display dialog "Want to get out of outer endless loop?" default ¬answer "")if userReply is "yes" then exit repeatend repeatrepeat until end [repeat]
Syntax
Repeat until trueBoolean(* code statements *)end repeatDescription
This form of
repeattakes theuntilkeyword and abooleanexpression, as in:repeat until countVar is true. The statements that are contained withinrepeatuntil...end repeatwill continue to execute until thebooleanconditional expression istrue. If the expression followinguntilistrue, therepeatloop is terminated and execution resumes with the statement followingend repeat. You could also exit thisrepeatloop using theexitstatement (seeexit). Note that whenrepeatuntilencounters atruevalue, the loop is immediately ended; its enclosed statements are not executed.Examples
This AppleScript shows a
repeat untilstatement that also contains anexitstatement:set theCount to 0repeat until (theCount = 5)if theCount = 4 then exit repeatset theCount to theCount + 1log theCountend repeatThis code increments a
theCountintegervariable by one with each cycle through the loop. The code includes a simpleifstatement that exits therepeatloop once thetheCountvariable reaches 4. Without theexitstatement, the variable would reach 5; the expression:theCount = 5would returntrue, and therepeatuntilloop would terminate. We keep track of the value oftheCountwith alogtheCountstatement. This displays all of thetheCountvalues in Script Editor's Event Log window.repeat while end [repeat]
Syntax
Repeat while trueBoolean(* code statements *)end repeatDescription
repeat whilekeeps executing its enclosed script code as long as thebooleanexpression followingwhileistrue:repeat while theTruth is true...end repeatIf the
booleanexpression that followswhilereturnsfalse, then therepeatwhileloop is terminated, and its enclosed script code will not execute anymore. Script execution then resumes after theend repeatpart of therepeat whilestatement.repeat whilehas the opposite effect ofrepeat until; it keeps executing as long as somebooleanexpression istrue, whereasrepeat untilkeeps executing until something istrue. You can also useexitwithinrepeatwhileto leave the loop. In general,Repeatloops can contain nestedrepeatloops, as well as other flow-control statements such asifandtell.Examples
This code shows how to use
repeat while:set theCount to 0repeat while (theCount < 5)set theCount to theCount + 1log theCountend repeatThe two code lines within this
repeat whilestatement block will continue executing (thus increasing the value oftheCountby one) whiletheCountis less than 5. Each cycle through the loop tests thebooleanexpression:theCount < 5and, as long as this expression returns
true, the enclosed code will execute again. The variabletheCountactually reaches 5 in the following code. This is because it is eventually incremented to 4, then the:repeat while (theCount < 5)executes and, since
theCountis still less than 5, the enclosed script code executes once more, increasing the variable's value to 5.repeat with {loop variable} from {integer} to {integer} [by stepVal] end [repeat]
Syntax
Repeat with loopVar from 1 to 10(* code statements *)end repeatDescription
This form of the
repeatloop executes a specified number of times over a range of values. A loop variable keeps track of how far therepeatloop has progressed in cycling over its range of loops. The loop variable increments by the value ofstepVal(or one by default if thestepValvariable is not specified) throughout each loop. This makes therepeat withstatement much more flexible and powerful thanrepeat {integer} times.You can take the value of the loop variable and use it in the executing code, as in the following example. Once thisrepeat withstatement reaches the end of its range, as in:repeat with loopVar from 1 to 10(10 is the end of the range here), then the
repeatloop terminates and code execution resumes with the statement followingend repeat. You can also use theexitstatement to terminate this loop (see "exit").repeat withis similar to the famous:for (i=0; i < rangeVar; i++)variation of the loop statement that JavaScript, Java, and C++ programmers are very familiar with.
Examples
This AppleScript loops through each character of a word to see if any character is repeated. It uses the loop variable to determine which character in the word to examine. This example also shows how you can specify any of the range values with expressions that return integers, instead of just literal integers:
repeat with loopVar from 2 to (2^2)set theString to the text returned of (display dialog ¬"Enter a word and I'll tell you which letter, if any, repeats first" ¬default answer "")set len to (length of theString) (* len is set to the number of characters in string *)tell application "BBEdit 5.0"repeat with loopVar from 1 to len (* repeat from char 1 to length of string *)if loopVar is equal to 1 then set charList to {} (*create a list to hold the examined characters *)set tempChar to (character loopVar of theString) (* tempChar is a single character in the string like 'o' *)if tempChar is in charList then (* if it's already in the listthen it appears more than once in the string *)display dialog "Your repeating character is " & tempCharexit repeat (* exits the repeat loop; finishes executing thescript *)end ifset charList to charList & tempChar (* no repeating chars yet so add the current char to the list *)if loopVar is equal to len then (* if this is true then we did not find any repeaters *)display dialog "You had no repeating characters!"end ifend repeatend tellend repeatThis script uses the BBEdit text editor because this app is good at examining text. The script gets a word from the user using the display dialog scripting addition (Part IV of the book discusses scripting additions). Then it uses a
repeat withloop to get each single character in thestringand store it in a variable of typelist(i.e.,charList). This is how the script keeps track of the characters it has already examined. TheloopVarof therepeat withstatement identifies individual characters of thestringwith an index reference form, as incharacterloopVar. IfloopVarwere 3 then the expression would evaluate tocharacter3, which is the third character in thestring. The code then checks thecharListlistof characters to see if the currently examined character is already in there. If the character is already in thelistthen it appears more than once in thestring. Then the script tells the user which character repeated and exits the loop:display dialog "Your repeating character is " & tempCharThis example shows this
repeat withloop with a specified step value:set theString to "Kindly give me every other word."set allWd to words of theString -- returns list of wordsset len to length of allWdset userMsg to ""repeat with indx from 1 to len by 2 -- repeat loops over the list by twoset userMsg to userMsg & return & (item indx of allWd)end repeatdisplay dialog "Here's every other word on its own line: " & return & ¬ userMsgrepeat with {loop variable} in {list} end [repeat]
Syntax
Repeat with listVar in myList(* code statements *)end repeatDescription
This variation of the
repeat withstatement iterates over alistof values, storing the current value in the loop variable. Once the lastitemin thelisthas been stored in the loop variable, the statement terminates and code execution resumes afterend repeat. If you have to examine alist's contents, this statement is a crucial part of your code. You can also use theexitstatement inside thisrepeat withstatement to stop executing code inside the loop. After any call toexit, code execution resumes after theend repeatpart. You do not have to declare the loop variable in any way; AppleScript creates this temporary reference variable for you. You can also get the loop variable's value later in the script, after the repeat loop has completed executing. The value will be a reference to the last item in the list:item 6 of {"Each", "word", "on", "a", "different", "line"}Using this form of repeat loop, you can get inaccurate results if the script is trying to compare the value of the loop variable with another value (such as a
stringorinteger). Instead, one of our technical reviewers recommends that you use syntax such as:set booleanVar to ((contents of loopVar) equals 1)" (* note the "contents of" part *)The value used in the part of the statement following the
inreserved word must be alist.Examples
This code takes each of the items of a
listand concatenates them to astring, which is then displayed to the user:set theString to "Each word on a different line"set theList to words of theString -- returns a list of wordsset displayString to "" -- initialize this stringrepeat with wd in theListset displayString to displayString & return & wdend repeatdisplay dialog displayStringwd as string -- this will return "line"AppleScript does not destroy the loop variable (
wdin the prior example) after the repeat loop is finished. Getting the value ofwdin the last example, after therepeat withstatement has done its job, returns:item 6 of {"Each", "word", "on", "a", "different", "line"}repeat {integer} times end [repeat]
Syntax
Repeat 10 timesDisplay dialog contriteStatementEnd repeatDescription
This loop statement begins with the reserved word
repeat, followed by anintegerrepresenting the number of times the loop should cycle, then the reserved wordtimesand anend repeat. (Therepeatofend repeatis optional.) You can use this variation ofrepeatif you do not need the finesse of the two more complex but powerfulrepeatconstructs, such as:repeat with loopVar in listOnce this loop has executed its enclosed script statements
integernumber of times, it terminates and the script execution resumes after theend repeat. You can also short-circuit thisrepeatloop by using theexitorexit repeatstatement. This causes the script flow to proceed to afterend repeat, regardless of whether the loop has cycledintegernumber of times.Examples
The following code does exactly what the last
repeatexample did; it works with each word in alist, finally displaying each of them on a different line. However, it uses the "repeat {integer} times" variation instead. The example also shows that you can use the return value of an expression for "{integer}" including anintegervariable, instead of just a literalintegersuch as 9:set theString to "Each word on a different line"set theList to words of theStringset len to length of theListset displayString to ""set counter to 0repeat len times -- len resolves to the length of the word listset counter to counter + 1set displayString to displayString & return & (item counter of ¬theList)end repeatdisplay dialog displayStringIn this example, the line
repeat len timesuses thelenvariable'sintegervalue to specify how many times therepeatloop should execute.lenrepresents the length of thelistof words that the code reassembles into anotherstring.return [return value]
Syntax
Return trueDescription
The
returnstatement returns values from functions or subroutines, just as it does in Perl and JavaScript. If you finish a function definition with justreturn, with no subsequent return value, then the function will return to where it was called in the script without returning an actual value. You can return any value, such as anumber,string,boolean, orlist:return trueIf you do not use
returnat all in a function then the return value of the function will be the result of its last statement, if the statement returns a result. (Chapter 9 is devoted to developing functions in AppleScript.)If you define a function with just
returnwithout a value and then try to set a variable to the return value of that function, the script will raise an error.Make sure not to confuse the
returnstatement with thereturnpredefined variable, which is a return character in a string such as:set theString to return & "Start a new line"tell simple statement
Syntax
tell app "SoundJam MP" to runDescription
You use the
tellstatement to identify the target of an AppleScript command:tell app "Photoshop 5.5" to runIn this case, run is the Photoshop application's command. The
tellsimple statement only takes up one line of code and does not need to be completed with anend tell. You use the reserved wordtell, followed by a reference to an object, such as the application "Finder," then the reserved wordtopreceding the actual command that you want to send to the object.tellstatements can be nested within each other, such as using atellsimple statement inside a compoundtellstatement (one that involves several lines of code and finishes withendtell).Examples
This code tells the Finder to open Photoshop only if a certain amount of memory is available to the computer:
tell application "Finder"(* largest free block is converted from bytes to megabytes then rounded off with the round scripting addition *)set freeMem to (round (largest free block / 1024 / 1024))if freeMem > 50 then (* only open PS if there is a free memory block >50 meg *)tell application "Adobe® Photoshop® 5.5" to activate (* tell simplestatement *)elsedisplay dialog ¬"Freemem = " & freeMem & " Not enough memory for gluttonous Photoshop!"end ifend tellThis example occurs within a compound
tellstatement that targets the Finder. If thelargest free blockproperty of the Finder (which identifies the largest free block of available RAM on the computer) exceeds 50MB, then Photoshop receives an activate command as part of atellsimple statement.TIP: If you are running AppleScript 1.4 or higher, you can create easy-to- remember aliases to invoke your favorite apps with the
tellstatement. For example, create an alias file for the SoundJam MP application, name this alias "SJ," and then store it in startup disk:System Folder:Scripting Additions. Now, when your AppleScripts include the code:tell app "SJ"the enclosed code statements direct their Apple Events to SoundJam MP. This saves a lot of typing!tell end [tell]
Syntax
Tell app "SoundJam MP"(* code statements *)end tellDescription
The
tellcompound statement identifies the target of an AppleScript command or Apple event (as intell app "Photoshop 5.5") followed by other AppleScript statements and anend tell. Thetellcompound statement can enclose any number of AppleScript statements, including othertellstatements and flow-control structures such asiforrepeat. You can identify any object in atellstatement, but unless the object is anapplicationobject such as FileMaker Pro or QuarkXPress, it has to be nested within anothertellstatement targeting the object's parent application. For example, if you want to use a statement such as:tell window 1 to closethen you would have to first target the application that "owns" the window, as in the following example:
tell application "BBEdit 5.0"(* hasChanged will be true or false *)set hasChanged to (front window's modified)if hasChanged thentell front window to close saving yeselsetell front window to closeend ifend tellThis script first finds out whether the front BBEdit window has been modified, and it stores this
booleanvalue (trueorfalse) in thehasChangedvariable. Iftrue, then atellsimple statement sends the front BBEdit window a close command (with a parameter instructing BBEdit to save the changes). If thistellstatement was not nested within thetell app "BBEdit"...statement, then AppleScript would not know which application's window the script was talking about, and an error would be raised. You could also write the program without a nestedtellstatement, as in this code:tell application "BBEdit 5.0"set hasChanged to (front window's modified)if hasChanged thenclose front window saving yes (* send BBEdit the close commandwithout another tell statement *)elseclose front windowend ifend tellTIP: With a feature that was new to AppleScript 1.4, you can add aliases to applications in the Scripting Additions folder of the System Folder. Give these aliases a short, easy-to-recall name like "fm" for FileMaker Pro, and you no longer have to spell out the app's name in the
tellstatement. You can just use the syntaxtell app "fm"...,and AppleScript will find the application.You can also use the predefined variables
me,my, anditwithintellstatements. AppleScript assumes that any command such as activate, close, or open within atellstatement should be directed to the application that is identified in thetellstatement. The exceptions are:
- A nested
tellstatement that targets a different application; in this case, any commands that are issued within this nestedtellare directed to its target app.
- A scripting addition or osax command, such as display dialog or round, can be issued within a
tellstatement in most cases without any qualifying or accompanying code requirements.
- Commands that are qualified with the
myorof mereserved words. This tells AppleScript that the command is a script command, as in:
set theTruth to my func( )
- Commands that target the app identified in the
tellstatement.
- Commands that are associated with a script object.
Examples
The script at the end of this section calls the script's own function inside of a
tellstatement. It also calls a function defined by a script object.itis an AppleScript reserved word that refers to the default target of Apple events, which is normally identified in atellstatement (Chapter 1, AppleScript: An Introduction, describes Apple events). This script is a little bigger than most included in the chapter, and I apologize to those like me who are partial to the use of only code fragments as examples. But it illustrates an important element of how you work withtellstatements--the visibility of commands.The script first identifies the text editor BBEdit in the compound
tellstatement, then tells this app to make a new window. The next line sets afirstLinevariable to the return value of a function call:InnerScript's getIntro( )Without the reference to the script object
InnerScript, AppleScript would assume that the getIntro function was a BBEdit command, because getIntro is called inside of atell app "BBEdit 5.0"statement. However, the script code indicates that this is the getIntro function of theInnerScriptobject. The following code would also work:set firstLine to getIntro( ) of InnerScriptIf you look down to the definition of the
InnerScriptscript object, you see that it defines a function (getIntro) that returns the value of InnerScript'sIntroproperty, which is astring: "I'm the first sentence." A lot is going on in theifstatement in the next example:if (my addLine(firstLine)) then display dialog "Text added ¬successfully: " & its nameThe script calls its own function (as opposed to a BBEdit command) called addLine. The reserved word
mydistinguishes this function (addLine) as defined by the script, not BBEdit, so AppleScript looks for the function definition in the script itself, rather than in BBEdit's dictionary. The following phrase would also work:addLine(firstLine) of meThe function inserts text into a BBEdit document and returns
trueif successful. Theifstatement responds to anytruereturn value from the addLine function by displaying a messagestringthat includesits name.Itis an AppleScript constant that refers to the default target for commands, which, inside thistellstatement, is BBEdit. Soits namereturns "BBEdit 5.0":tell application "BBEdit 5.1"make new window with properties {name:"Front Win"}(* the InnerScript script object is defined beneath this code *)set firstLine to InnerScript's getIntro( )(* addLine is the 'outer script's' function, not InnerScript's *)if (my addLine(firstLine)) then display dialog ¬"Text added successfully: " & its nameend tell(* user-defined function addline( ) *)on addLine(txt)trytell application "BBEdit 5.1" to insert text txton errorreturn falseend tryreturn trueend addLine(* script object definition *)script InnerScriptproperty Intro : "I'm the first sentence."on getIntro( )return Introend getIntroend scripttry [on error] [number | from | partial result | to] end [error | try]
Syntax
Try(* code statements here *)on error errTextdisplay dialog "An error:" & errTextend tryDescription
tryrepresents AppleScript's all-important error-trapping capability. If any of the statements that are enclosed in atry...end trystatement block raise an error, then AppleScript catches the error and prevents it from taking down the whole script. Aftertrycatches the error (similar to Java'stry...catchexception-trapping syntax), the script has the option of adding inside thetryblock the reserved wordson errorfollowed by any code that should execute in response to the error.TIP:
on erroris optional inside oftrystatements beginning with AppleScript 1.4.
The program will then resume following the
end trypart of thetryblock, as though nothing happened. Without atryblock, AppleScript's default error behavior is to display an error message in a dialog box then cancel the running script.tryonly catches one error at a time. By using theon errorstatement and its numerous parameters, you can uncover all kinds of details about the error, but you do not have to use it. In the OS versions previous to Mac OS 9, Script Editor does not compile a script that includes atryblock without anon errorstatement.Examples
This example traps any errors caused by invalid data entered by the user, and then goes on its merry way without explicitly responding to any errors.
trystatements can be used inside and outside of your own subroutines, script objects, and libraries; they can nest other statements such asif,repeat, andtell. In fact, your entire script can run inside of atrystatement, and thetryblock can contain othertrystatements:tryset userReply to the text returned of ¬(display dialog "Try your best to enter a number." default answer ¬"")set invalidNum to falseset userReply to userReply as realon errorset invalidNum to trueend tryif invalidNum thendisplay dialog "That's the best you can do?!"elsedisplay dialog "thanks for entering: " & userReplyend ifThis script politely asks the user for a number; it sets the reply to the variable
userReply. This variable is then coerced from astringto arealtype, which raises an error ifuserReplyis not a valid number. For example, "a10" couldn't be converted to a valid number. AppleScript displays this error and stops running the script if we do not catch it in thetryblock. If the error is raised, the statements that appear betweenon errorandend tryexecute. In this case, the script sets abooleanvariableinvalidNumtotrue. Remember, the script does not have to use theon errorstatement part oftryin Mac OS 9 or OS X. It can simply use atryblock to prevent any errors from crashing the script, then go on blithely executing the rest of the code. Theerrorhandler of thetrystatement contains five variables from which you can obtain information about any errors. The following code shows two of the many ways that you can usetry. The first demonstration catches but then skips over any errors that might be raised while it executes its code. The second use oftrydeploys theon errorhandler to grab all the data that it can about the error and display it to the user:tell application "SoundJam? MP"tryactivate (* will raise an error if SoundJam isn't on the computer,but the program will just keep going *)end trytryset allPlay to playlist windows -- a list of playlistsrepeat with pl in allPlayif (name of pl) is "tranceControl" then set mainPlay to plend repeatset trackNameList to name of (tracks of mainPlay)set trackMsg to ""on error errMsg number errNum from objErr partial result errList ¬to errClass(* display the error message, error number, the object that is thesource of the error, any partial results, and class information *)display dialog errMsg & ": " & errNum & return & "Source of ¬error was: " & objErr & return & "Here are any partial ¬results: " & errList & return & "If coercion failure it ¬involved a coercion to: " & errClassreturn -- exit the programend tryrepeat with nam in trackNameListset trackMsg to trackMsg & return & namend repeatdisplay dialog "The MP3 track names in the main playlist are: " & ¬return & trackMsgend tellIn the prior example, if any statements in the second
tryblock raise an error, then theon errorhandler displays error information using all five parameters ofon error. AppleScript gives these parameters a value (e.g., the error description and number) for you if any errors are raised. The values for thepartial listandtoparameters are empty lists if there are no partial results or coercion problems associated with the error. Here's a rundown of the five optionalon errorparameters:
- The first nonlabeled parameter is a
stringdescribing the error, as inonerror errMsg. The variableerrMsg, which you create, contains the error message.
- The
numberparameter contains the error number, as inon error number errNum. Use thenumberlabel followed by your own variable to contain the number.
- The object that was the source of the error is labeled with the keyword
from. An example ison error from objErr. You create the variable following the reserved wordfrom, and if AppleScript can identify the object source of the error, it will store the name of the object in that variable.
- If the error-causing operation involved getting a
listof values, and it was successful in getting some of thelistvalues, then thislistis stored in the variable labeled with the reserved wordspartial list. The content of this variable is of typelist.
- If the error was caused by a faulty coercion, than the class that the script failed to coerce some value to is identified in the variable following the reserved word
to, as inon error to errClass. The identifiererrClasscontains the word describing the class, such asboolean,list, orreal.
using terms from end [using terms from]
Syntax
Tell app "Finder" of machine "eppc://192.168.0.2"Using terms from app "Finder"Get largest free blockEnd using terms fromEnd tellDescription
This block structure allows the scripter to compile a script using local applications and to have the option to run the script on remote machines using a TCP/IP or AppleTalk network. Chapter 25, File Sharing Control Panel, describes how to use the Mac's powerful new program linking technology to run distributed AppleScripts over TCP/IP networks.
using terms fromis new to AppleScript 1.4. Similar to thetellblock, it takes an application object as a parameter, as in:using terms from app "Finder"You use this construct to help avoid the display of the Script Editor dialog box that asks for the location of the target application in a
tellblock. This dialog box usually displays when the script is first compiled and then whenever the script is executed on a different machine. If you have not encountered this dialog box yet during AppleScript hacking, then you are either lucky or just haven't done very much AppleScripting.Examples
using terms fromis best illustrated with this example, which dynamically targets whatever machine you want, but compiles using terms from the local machine:set theMachine to "eppc://" & the text returned of ¬(display dialog "Enter your IP address:" default answer "")trytell application "Finder" of machine theMachineusing terms from application "Finder"set freeMem to (round (largest free block / 1024 / 1024)) as ¬stringdisplay dialog freeMemend using terms fromend tellon error errMsgdisplay dialog errMsgend tryThis script targets the Finder on a particular Apple machine (depending on what the script user enters as the machine name or IP address). The script compiles, however, using its local Finder app. If the user enters an invalid or nonexistent IP address, then an error is raised and reported at the end of the
tryblock. When targeting applications over a TCP/IP network, you have to precede the IP address with the protocol"eppc://", which stands for "event program to program communications":tell application "Finder" of machine "eppc://192.168.0.2"with timeout [of] {integer} second[s] end [timeout]
Syntax
With timeout of 15 secondsEnd timeoutDescription
The
with timeoutstatement allows you to alter AppleScript's default 60-second time limit for the Apple events that are sent to applications. Normally, if an application fails to respond to an Apple event within 60 seconds, AppleScript raises an "Apple event timed out" error and stops running the script. You can make this time limit shorter, say 30 seconds, by using the syntax:with timeout of 30 seconds...end timeoutYou enclose the
with timeoutstructure in atryblock to trap and report any timeout errors (see "try").with timeoutonly applies to the following types of commands. In other words, thewith timeoutlimit is ignored unless the command is one of these types:
- Commands sent to applications targeted in
tellblocks
- Scripting addition commands that have application objects as parameters (not too many osaxen have application objects as parameters)
- Scripting addition commands that are called inside of
tellstatements that target other applications
Examples
The following example times out if you just let the display dialog dialog box sit there for over five seconds. This happens because the display dialog scripting addition is positioned inside the
tellblock targeting the Finder. Pull the scripting-addition command outside thetellblock, and the script does not time out. Again,with timeoutdoes not work with scripting-addition commands unless the command is part of atellblock targeting another application, or it takes an application object as a parameter:try -- catch any timed out errorswith timeout of 5 secondstell application "Finder"get versiondisplay dialog "fast" (* let this sit for about 5 secs and raisean error *)end tellend timeouton error -- will be called if 'with timeout' block times outdisplay dialog "Sorry, the operation timed out"end tryTIP: If an AppleScript that sends an Apple event to another application times out, the Apple event itself is not cancelled (with or without a
with timeoutstatement). So the script might have timed out, but the application could still eventually respond to the Apple event that the script sent to it.with transaction [session object] end [transaction]
Syntax
With transaction(* code statements here *)end transactionDescription
with transactionis designed to group together its enclosed statements and commands by assigning each of them a single transaction id. If a database application supportswith transaction, for instance, than it knows which Apple events or commands share a transaction and can initiate an appropriate response, such as locking the particular table from other users until the transaction is complete. What is a transaction? A transaction gathers together a group of operations and declares, in essence, that, "we're all in this together--if one of us fails, then we all fail. We won't signal a successful completion until we all succeed."The
with transactionstatement itself, beyond assigning the transaction id, does not have any other transactional-related capabilities such as rolling back all of the statements if one of the statements (e.g., a statement that updates or alters a database file) within the transaction fails. Any behavior that commits or rolls back database changes that are part of a single transaction would have to be initiated by the database system itself (the database program that AppleScript is scripting).with transactiononly works with the database programs that support this statement.Examples
To show what the
with transactionstatement looks like, the following AppleScript requests the first database record from an open FileMaker database. It encloses this request in awith transactionblock:tell application "FileMaker Pro"with transactionget the first record in the database named "Mydatabase"end transactionend tellIn this case, if you watch the Script Editor Event Log window as you run the script, FileMaker converts the
withtransactionstatement to its ownbegin transactioncommand. This command returns aninteger, the transaction id, such as 2812565. You can include an optional session-object parameter with thewithtransactionblock, but not all applications support it. If you want to use AppleScript, transactions, and databases, then you have to evaluate the particular database system's support forwithtransaction.
Back to: AppleScript in a Nutshell
© 2001, O'Reilly & Associates, Inc.
webmaster@oreilly.de