Scripting Mac OS X
Pages: 1, 2, 3
Appendix: List of Common Administrative Commands for Shell, Perl, and AppleScript, Listed Side-by-Side
- Creating an Executable Script
-
To create an executable AppleScript, you must open /Applications/AppleScript/Script Editor, write the script that passes the Check Syntax operation, then select File->Save As... and change its format to Application. You also want to check Never Show Startup Screen.
To create a Perl script, create a new plain text file and on the first line put the "shebang" (
#!) and then the path to the Perl interpreter, like so:#!/usr/bin/perl # code goes here # etcYou must also make sure it has the execute permission set.
To create a shell script, make it just like you would a Perl script, except replace the path to the Perl executable with the path to the shell-script language you will use. For example:
#!/bin/sh # code goes here # etc - Executing a Script
-
Perl, shell, and AppleScripts can all be launched in the Terminal by typing in the path to the script, either a full path or a relative path. For example, if your script is on your Desktop:
/Users/yourname/Desktop/myscriptor if you open a new Terminal window, it defaults to /Users/yourname, so you can just type:
./Desktop/myscriptAppleScript files saved as Application format can also be double-clicked. Perl and shell scripts cannot be double clicked, unless you add .command to the end of the script. However, a script that is opened in this way will open the Terminal, and then execute in a new Terminal window.
There are third party utilities such as DropScript, Pashua, and Platypus that will turn a script into an application.
Perl and shell scripts that do not have execute permissions can also be executed in the Terminal by typing the path to the Perl or shell interpreter, and then the path to the script. For example, type this in the Terminal:
/bin/sh /path/to/scriptor
/usr/bin/perl /path/to/script - Creating Variables
-
In AppleScript:
set theVariable to "something"In Perl:
$theVariable = "something";In Perl, variables always start with
$when you create them and when you access the contents.In a BASH shell script, create a variable using the variable name, without a
$, and with no spaces after the=, like this:theVariable="something"To get at the contents of a BASH variable, you must place a
$before the variable, like this:echo $theVariableOther script environments, such as TCSH, set variables differently. TCSH is as follows:
set theVariable="something" echo $theVariable - Comments
In AppleScript, a comment begins with
--.-- this is a commentIn Perl and shell scripts, a comment begins with
#.# This is a comment- Arrays (or
Lists) -
In AppleScript, an array is called a
Listand is created like this:set theList to { "item1", "item2", "item3"}Access an item of the list like so:
display dialog item 1 of theListGet the number of items like so:
set x to count of every item of theListLoop with the list like this:
repeat with i in theList display dialog i end repeatIn Perl, you create a list like this:
@theArray = ("item1", "item2", "item3");Access an item of the array like so:
print $theArray[0];To get the number of items in the array, use this:
$number_of_items = @theArray;Loop with the list like this:
foreach $i (@theArray) { print "$i\n"; }In a BASH shell script, create an array like so:
theArray=(item1 item2 item3)Print out the first item like so:
echo "${theArray[0]}"Get the number of items like so:
echo "${#theArray[@]}"Loop with the list like this:
for i in ${theArray[@]} do echo $i done if-thenCondition Blocks-
In AppleScript, an
if-thenblock looks like this:set something to "blah" if (something is equal to "bla") then display dialog "something is bla" else if (something is equal to "blah") then display dialog "something is blah" else display dialog "something is not bla or blah" end ifIn Perl, it looks like this:
$something = "blah"; if ($something eq "bla") { print "something is bla\n"; } elsif ($something eq "blah") { print "something is blah\n"; } else { print "something is not bla or blah\n"; }Also, in Perl, text comparisons are done with
eq(equal), orne(not equal). Number comparisons are done with==(equal to),!=(not equal to), etc.In BASH, it looks like this:
something="blah" if [ "$something" = "bla" ]; then echo "something is bla" else if [ "$something" = "blah" ]; then echo "something is blah" else echo "something is not bla or blah" fi fiIn Perl, spaces in condition statements don't always matter. In BASH, you must have the spaces around the brackets and the equal sign.
- Check to See if a File Exists
-
In AppleScript, to check to see if a file exists, you do this:
tell application "Finder" if (file "Hard Disk:path:to:file" exists) then -- do something end if end tellIn Perl, you do this:
if ( -f "/path/to/file" ) { # do something }In BASH, you do this:
if [ -f "/path/to/file" ]; then # do something fi - Combining Strings
-
In AppleScript, you combine strings with
&:set theVariable to "c" set x to "a " & "b " & theVariableIn Perl, you do it with a period:
$theVariable = "c"; $x = "a " . "b " . $theVariable;If you use double quotes, you can place variables straight in the quotes and it will be replaced with the variable value, like this:
$x = "a b $theVariable";or, to put text at the end of the variable:
$x = "a b ${theVariable}d";If you really want a
$, you have to escape it:$x = "a b \$theVariable";Or use a single quote:
$x = 'this that $theVariable';You have to be careful with special characters in a double quote. If you are worried about using special characters, you can either use a single quote or just test escaping it.
# this is a test $x = "test: &@!$?"; print $x;In BASH, you combine variables like so:
variable1="a " variable2="b " variable3=${variable1}${variable2}c echo ${variable3}
James Reynolds is a member of the University of Utah's Student Computing Labs Mac Group. His main duty is the deployment of Mac OS X. Most of his responsibilities include the OS customizations, scripts, and security of the Mac OS X lab and kiosk computers supported by SCL.
Return to the Mac DevCenter
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 28 of 28.
-
Google via Terminal (user input problem)
2007-05-19 15:29:25 paymon [Reply | View]
I wrote this script the other day but it doesn't quite work well. The script is so:
-----------------------------
#! /bin/sh
read F1 F2 F3 F4 F5 F6 F7 F8 F9 F10
open "http://google.com/search?q=%20site:wikipedia.org%20$F1%20$F2%20$F3%20$F4%20$F5%20$F6%20$F7%20$F8%20$F9%20$F10"
-----------------------------
Once it's done try to modify your search in the Google's search result page. You will notice an empty space in the search field (given that you entered less than 10 words) and even worst I can't do phrase searching; try for instance "Google via Terminal".
-
Scripting Internet Connect
2005-03-07 08:03:21 Larriestyle [Reply | View]
Hi. I am currently trying to write an applescript script that will set a users DUN, to enable them to dial into a specified number, URL and essentially act as a mode of getting the user on line through a narrowband phone line.
I have tried various methods with no success. Does anyone know if this is at all possible to do this with Applescript?
How should l be setting the phone number, url etc? -
Scripting Internet Connect
2005-03-07 08:50:16 jamesreynolds [Reply | View]
I am unfamiliar with users' DUN. Dial up number? Is this something set in System Prefereces?
The way you find out if an application is AppleScriptable is you drag it (the app) on top of Script Editor and let go. Script Editor will open the application's dictionary if it is AppleScriptable or give you a message if it is not AppleScriptable. Even if it isn't AppleScriptable, you could probably do what you wanted with Apple UI scripting:
http://www.apple.com/applescript/uiscripting/
http://www.tidbits.com/tb-issues/TidBITS-670.html#lnk3
If it is scriptable, you just have to read the dictionary to find out how to do what you want.
If you are scripting the network settings, AppleScript might not be the best tool. Look into scutil (type "scutil --help") or ncutil (http://deaddog.duch.udel.edu/~frey/darwin/ncutil.php).
-
Making and running scripts
2004-10-01 15:04:19 yush [Reply | View]
I am unable to run scripts on OSX. Here is what I did:
I created a file script (no extensions) as follows:
#!/usr/bin/perl
echo "Hello"
And then I save it. After that I go to my shell and try running this file and I get the following message:
bash: /Users/yush/script: /usr/bin/perl: bad interpreter: Permission denied
Any idea what I am doing wrong, and how I can fix this? -
Making and running scripts
2004-10-01 15:29:37 jamesreynolds [Reply | View]
I haven't seen this exact error, so I am not sure off hand. But here is the debugging steps I would go through.
What did you use to edit the script? Are you sure it saved a text file? Are you sure it has Unix line endings?
How did you execute it? Did you change the permissions on the file so that it is executable? Did you specify the interpreter by typing:
bash script
Or did you type
./script
or
/Users/yush/script
I can tell that bash tried to execute the script, because of the message (the first word is bash). That shouldn't be happening because you specify perl on the first line of the script.
Also, echo is a shell command, not a perl command, so it wont work anyway. I get this message:
String found where operator expected at ./script line 2, near "echo "Hello""
(Do you need to predeclare echo?)
syntax error at ./script line 2, near "echo "Hello""
Execution of ./script aborted due to compilation errors.
The perl command is print (and perl lines have to end with ":"):
#!/usr/bin/perl
print "Hello\n";
-
loginhook
2004-03-04 10:10:02 yantheman [Reply | View]
Good article thanks.
I'm stuck running commands in my login script in 10.3 and I'm having trouble with paths.
Here's my test login hook script:
#!/bin/sh
/usr/bin/touch ~$1/Library/Preferences/thisisatest.txt
exit 0
# end
'sudo testscript.sh yan' gives 'touch: /Users/yan/Preferences/thisisatest.txt: No such file or directory'
The command works fine out of the script.
I need to use a relative path to find the users home folder because some of my users are in /Users and some are in /network for networked home folder. Any ideas would be appreciated. -
loginhook
2004-03-04 13:26:16 jamesreynolds [Reply | View]
$1 for a shell script is the first variable passed into the script. The Loginwindow passes in the username only, not "/Users/". I don't know why you got the error you did. I got:
touch: ~james/Library/Preferences/thisisatest.txt: No such file or directory
If I changed the script to:
/usr/bin/touch /Users/$1/Library/Preferences/thisisatest.txt
then it works.
-
loginhook
2004-03-04 14:26:25 yantheman [Reply | View]
This works for me to, but what if users folders aren't always in located in /Users? This is a common with dynamically mounted home folders. The path to the users folder is sometimes /network/volumes.. etc.
Can a solution be found to use relative paths here?
Loginhook script runs as root.. Interestingly this command in the script works. Prefs management in Mac OS X is excellent.
/usr/bin/sudo -u $1 /usr/bin/defaults write com.apple.print.PrintingPrefs DefaultPaperID -string "iso-a4"
-
Tk Aqua
2004-01-05 10:21:17 anonymous2 [Reply | View]
With TclTkAqua, you can now build a complete GUI in a portable scripting language, and have the application run the same way on Windows, UNIX, Mac OS X, and Mac OS 9... but with the native look and feel of each operating system and using native buttons and controls.
If you're working in a multiplatform environment, this is no small advantage.
-
language comparisons
2003-11-15 01:49:14 anonymous2 [Reply | View]
This was excellent the way there were comparisons to other languages. I'd like to see more articles dedicated to this kind of thing. say a simple task (no Hello World please) compared in lets say, BASH , TCSH, Perl, Python, Ruby, Java, PHP and Applescript/osascript to see how the languages compare in complexity of their syntax and what each could be used for for maximum flexibility.
This could get more people interested in these languages and could set off a trend in some of the lesser known ones.
O'Reilly keep these kind of articles coming. PLEASE!
-
more difficult defaults
2003-11-14 12:17:27 eoligarry [Reply | View]
Anyone know how to use defaults to change the download folder? The preference is under the "Web" tab of the "Internet" pane. using 'defaults read com.apple.internetconfig' (where the preference is stored) gives some weird stuff and mentions both "ic-added" and "ic-deleted". Anyone know how to make sense of that, and how to change the default download folder? -
more difficult defaults
2003-11-14 13:01:50 jamesreynolds [Reply | View]
I believe defaults can only read the first key, in the case of com.apple.internetconfig, it would be "Version 2.5.3" in Jaguar and "Version 2.5.4" in Panther. Then it prints out the entire preference basically. I would love to know that I am wrong, because then it makes alot of things possible, but I don't think I am wrong.
Because defaults can only read the first key, you would have to get the entire output of the preference file, parse it, make your change, then save it again. This is much easier to do with Cocoa or even CoreFoundation (maybe easier...).
The defaults utility is just a really simple XML reader/writer. "Really simple" can not be emphasized, and you know this if you have used a real xml parser. See http://www.xml.com/.
There is an XML parser for perl, and Python parses XML also. However, neither of them parse XML out of the box. The XML perl module has to be downloaded from CPAN. I think the source code might even need to be tweaked with to get it to compile--see some recent posts to the macosx@perl.org mail list. I believe Python is missing some libraries (I'm guessing here from what I remember seeing on some lists). This was Jaguar also.
In summary, I know of no easy "out-of-the-box" way to read/write XML from a script. If anyone knows, please post.
When I have complex preference I need to change, I just open and read the raw xml file, find what I want to change, change it (writing raw xml) and then save it.
But here is what will really stop you from doing what you want. The DownloadFolder key has a dictionary with one item: the key "ic-data" and its data. The data probably represents an alias, but I am not sure.
So since the preference file doesn't store the download location as a path, that doesn't leave much option for scripting because AFAIK, there is no way via command line to create or get a textual representation of an alias.
Well, ok. I have a tool that will do this for the dock, but it isn't public yet. And it only works for the dock right now. No time frame for the release other than "not soon"
-
Also, take a look at Tcl
2003-11-13 09:16:21 anonymous2 [Reply | View]
http://www.tcl.tk/ is another scripting language available on MacOS X for scripting purposes. In fact, take a look at
http://www.macdevcenter.com/pub/a/mac/2003/10/24/unixapps.html
for a macdevcenter article from someone whose
commercial MacOS X product uses Tcl.
-
Defaults Tips & Tricks
2003-11-10 15:49:22 anonymous2 [Reply | View]
Just read your O' Reilly article, good stuff! I have one comment. One thing I learned from my last rev of LoginWindow Manager is that its probably best to use the system-domain loginwindow pref instead of root's:
sudo defaults xxx /Library/Preferences/com.apple.loginwindow
instead of
sudo defaults xxx com.apple.loginwindow
It doesn't really matter which you use, but the /Library/Preferences item is easier to get to from a GUI and just seems like "the right thing to do"®. Incidentally, you can use the defaults command to read/write just about any xml file, whether its in the defaults path or not:
[anonymous:~] defaults read /System/Library/CoreServices/SystemVersion
{
ProductBuildVersion = 7B85;
ProductCopyright = "Apple Computer, Inc. 1983-2003";
ProductName = "Mac OS X";
ProductUserVisibleVersion = "10.3";
ProductVersion = "10.3";
}
[anonymous:~] defaults read /System/Library/CoreServices/SystemVersion
ProductVersion
10.3
And you can even use it to manage ByHost items that are, for
example, in a user template folder:
sudo defaults -currentHost write
/Library/Management/default/Library/Preferences/ByHost/com.apple.screensaver
idleTime 300
And that's really nice because it allows you to avoid dealing with the actual name of the file. Pretty damn cool!
-
What about the alternatives?
2003-11-10 00:30:58 brianl [Reply | View]
The author states he knows shell and Perl. He should have at least mentioned the Apple supplied alternatives he doesn't know: Python and Ruby. -
What about the alternatives?
2003-11-10 11:27:35 anonymous2 [Reply | View]
I don't think the author was stating these are the only scripting options or that they are best. Just that they are what he uses and is familar. -
What about the alternatives?
2003-11-10 13:35:42 jamesreynolds [Reply | View]
Yes, that is correct. I almost listed the languages in the first paragraph, but decided not to. Philip Rinehart, who shared the presentation with me "Migration to Mac OS X, A Case Study of Higher Education Institutions," uses Python and he likes it. Even though I have never used it, I would like to add it to the side by side comparision of common script tasks that I wrote for AppleScript, Perl, and shell. I think comparing one language that you know to a new one is a way good way to start learning a new language.
-
Getting Back
2003-11-09 16:07:24 johnmcadams1 [Reply | View]
Thanks for the great article. OS X is really an incredible OS for scripting.
To get back to before the tutorial I used:
sudo defaults delete com.apple.loginwindow LoginHook
That seems to have stopped the login hook. Is there anything else we should look to clean up? -
Getting Back
2003-11-10 11:30:26 anonymous2 [Reply | View]
Nope, other than removing your scripts, if you want. Or you might need to double-check for possible unknowns/bugs in your scripts that modified something you didn't expect.
This basically modifies the file:
/var/root/Library/Preferences/com.apple.loginwindow.plist
-
end-of-line character and sudo
2003-11-08 08:28:48 hayne [Reply | View]
A small correction:
The end-of-line-character in UNIX is ASCII 10 - not 8 as reported in the article. (man ascii)
And a recommendation:
The article suggested using 'sudo /bin/sh' in order to run a script as root. It would be better to use 'sudo' instead. You could do 'sudo name-of-script' and it would run as root. The use of 'sudo' for individual commands/scripts is generally recommended instead of running a shell as root. This limits exposure to problems and has the side benefit that everything you do is logged. -
end-of-line character and sudo
2003-11-10 13:45:39 jamesreynolds [Reply | View]
I prefer "sudo -s" or "sudo /bin/sh" but you can also use "sudo" before each command. That method is the general rule because it is safer, that is, it times out after a few minutes rather than leaving a command line with root access and as mentioned, the system does log each sudo command to /var/log/system.log.
If you were following the tutorial, and you wanted to execute each line in the terminal, you would type:
sudo rm -rf "/Users/labuser"
sudo ditto -rsrcFork "/System/Library/User Template/English.lproj" "/Users/labuser"
sudo chown -R <username>:staff "/Users/labuser"
instead of:
sudo /bin/sh
rm -rf "/Users/labuser"
ditto -rsrcFork "/System/Library/User Template/English.lproj" "/Users/labuser"
chown -R <username>:staff "/Users/labuser"






-----------------------------
#! /bin/sh
read F1 F2 F3 F4 F5 F6 F7 F8 F9 F10
open "http://google.com/search?q=%20site:wikipedia.org%20$F1%20$F2%20$F3%20$F4%20$F5%20$F6%20$F7%20$F8%20$F9%20$F10"
-----------------------------
Once it's done try to modify your search in the Google's search result page. You will notice an empty space in the search field (given that you entered less than 10 words) and even worst I can't do phrase searching; try for instance "Google via Terminal".