Applescript/Terminal.app Solutions
by Dick Guertin
I've written this document to describe some of the problems I've faced with Applescript, and the solutions to many of those problems. Let me start with a short list of the problems.
1. How to use a Terminal window for the process, and eliminate it on completion.
2. How to wait within the Applescript code for a Terminal process to complete.
3. How to deal with "sudo" commands, i.e. administrator privileges.
These three basic problems lead to other problems, which also had to be solved. To show you my solutions, here is a sample Applescript script ("appsync.applescript" file):
The third line you see in this example is the setting of commandString, the Unix script I intend to execute within a Terminal window. A sample of that Unix script file, "appsync.exe", is shown next:
OK, now what is happening, and why is it being done like this? Let's start with the "appsync.exe" Unix script directly above. It needs to use "sudo" to get a list of the Firewall settings. That may cause a dialog to occur asking for Password. The problem with dialogs is there's no telling when the user will respond. And since we'd like to close the window used for this process, we need to wait within the Applescript code for the Unix script to complete. But how long do we wait?
The answer is in the "/tmp/appsync.zap" file. As long as it exists, the Unix script is running. We create the file when we start, and remove it when we exit. So the existence of this file can act as a signal to the Applescript. We place the "sudo" command in a function in case we cancel. We also placed a "sudo -k" near the end to cancel any administrative privileges obtained during the execution of this Unix script.
This basically answers problem 3. We'll use "sudo" in the Unix script executed from within the Applescript. What about the window we'd like to have to see what is happening? That is taken care of by System Events and Terminal. First, we have to insure that Terminal is running, or get it running. That is done by the "System Events" tell block. There's a very important global boolean variable involved called "myActive". This flag indicates if Terminal had to be launched (true), or was already running (false). We use that at the end to quit from Terminal if we had launched it.
We want to use a window in Terminal to handle the Unix command, but we don't want to disturb any existing windows. That is handled by: if not myActive then do script "" -- which creates a new window if Terminal was already running. We'll discard this new window as we complete the Applescript.
Now, do the command: do script commandString in front window
This starts the Unix script, "appsync.exe", in this example. We don't know when it will end, but we do know that it creates the /tmp/appsync.zap file, so if we give it sufficient time to get started, that file should exist, and we can wait for it to go away. That's what the "Finder" tell block is doing.
It sets a time limit, just in case something goes wrong, and tests the "existence" status of the file using a mySync flag and "do shell script localTxt". Note that localTxt is a string defined globally at the beginning of the Applescript. It is a /bin/sh form (bash form) of an if command which tests if a file exists, and exits with 0 or 1 (0 if it exists, 1 if not). That exit value is assumed to be 0, and mySync flag is set to true. But if the exit value isn't 0, "on error theError" is triggered, and we set mySync to false to terminate a repeat loop that delays for 1 second and does the file test. That's basically the entire contents of the "Finder" tell block.
Once the "/tmp/appsync.zap" goes away (or the timeout occurs), we clean up Terminal by first closing the window we've been using, and then, if we launched Terminal (myActive is true), we quit from Terminal.
Broken into distinct parts: the Applescript starts Terminal if necessary, gets a new window if necessary, sends the Unix command to Terminal with "do script ... in front window", waits for the script to complete, and cleans up Terminal.
Of course, the Unix script has a file for communication, so the Applescript and Unix script must be in agreement on that. But as you can see, this mechanism allows you to write an "application" that does a lot of it's work in Terminal by means of one (or more) Unix scripts. For the purposes of this document, I've kept all the file names the same, with different suffixes. "appsync.exe" is stored in your home directory. "appsync.applescript", the Applescript, can be stored wherever you like. If you create an application, it should be "appsync.app" built from "appsync.applescript" using the Apple Script application. Just "Save As..." and select the File Format: application, with Run Only as the only checked option. Put the "appsync.app" file wherever you like, including the Dock.
Lastly, your Unix script could create several "appsync.suffix" files with different suffixes. They are all deleted by the "rm -rf appsync.*" command at the end. One thing you can do is bypass the Password prompt for "sudo" by doing a special version of "sudo" at the top of the function block.
echo "yourpassword" >/tmp/appsync.key
sudo -p 'Working' -S </tmp/appsync.key cd $PWD
This exposes your password to anyone who reads your "appsync.exe" file, but that may be acceptable. Or, you could encrypt it somehow and store that in a permanent file. Then your script would have to read that file into a variable and decrypt it and use the result variable as a replacement for yourpassword in the echo command.
For an alternative bypassing the "sudu" prompt, and additional tips and tricks, see Applescript Sudo Solutions.
Final note: I prefer to store Applescript code as type "text" with the ".applescript" suffix. Storing as a "script" with ".scpt" makes it an unreadable file by normal text editors. As a text file, you can "grep" it, or a collection of applescripts, for any desired content.