Whether you’re a blogger working on a roundup or a student doing some research, you’ve probably run into the need to create a list of websites based on your currently open Safari tabs. Let’s learn how to write an AppleScript that will handle this process for us.
A sample of what the script should output based on the open tabs in Safari.
What We’re Going to Learn
The script that we’re going to write is actually fairly simple so if you’re just getting started with AppleScript, this is the perfect project to cut your teeth on.
Whether or not you actually need a tool that will create a list of Safari tabs is irrelevant. The important thing here is to learn from how we approach this task.
The important thing here is to learn from how we approach this task.
We’re going to hit on a number of AppleScript constructs that are critical in your journey to becoming an automation pro: variables, “repeat with” loops, working with strings, escaping characters and a lot more.
Step 1. Create an Algorithm
The first thing that you want to do when working with any programming project is see if you can sketch out an algorithm. That word sounds pretty intimidating but really it’s just a fancy way to say that we need to work out the steps of what we’re going to be doing.
It’s just a fancy way to say that we need to work out the steps of what we’re going to be doing.
To create an algorithm for our project, we have to have a basic understanding two things: our ultimate goal and the steps necessary to reach it. The latter part is a little tricky because it does require some specific knowledge of how to approach the problem from a programming perspective, which may or may not be intuitive depending on how your brain works.
Let’s first analyze our goal, what do we want to accomplish? The end result that I’d like to shoot for is a TextEdit document that contains a list of the open tabs in Safari. Each tab should be formatted as an HTML link with the title of the tab set to the link text, like this:
<a href="www.siteurl.com">Tab Title Here</a>
With this goal in mind, we can begin to sketch out our algorithm by thinking through the processes that need to happen for us to accomplish that goal. At this point, don’t worry about perfection. We’ll mentally refine this process as we come across challenges in the coding process, this is merely to help us get a feel for what our script will look like.
- Count the number of Safari windows
- Count the number of tabs in each window
- Create a loop that goes through every tab of every window
- Grab the name of the current tab
- Grab the URL of the current tab
- Store tab name, URL and other desired text in a variable
- Open TextEdit
- Create a new document
- Set the text of the new document to the variable that contains our string of information
Step 2. Create a Safari Tell Block
Now that we’ve got our basic outline of the steps that we’re going to require, it’s time to jump into coding. The first several steps in our algorithm all had to do with Safari, so the first thing we need to do in our script is create a tell block for Safari.
tell application "Safari" end tell
Tell blocks are how we target who/what it is that we’re talking to in our script, in this case it’s the application Safari.
Tip: Don’t forget to close your tell blocks with “end tell”, your script will throw an error without this.
Step 3. Create Initial Variables
One of the first things that I like to do in any script is create any variables that I know I’ll need. This helps set the stage for the rest of the script and keeps things organized.
At this point, I know that I’ll need to set up three different variables: one for the window count, one for the tab count, and one for the resulting document text.
The tab count variable needs to update as we cycle through the windows, so that will need to be nested in a loop, but the others we can take care of now.
tell application "Safari" --Variables set windowCount to number of windows set docText to "" end tell
Setting the Window Count
After the comment, the first line of code creates a variable called “windowCount”. Notice that this is very descriptive, not something generic like “myVariable”. Always try to name your variables something useful so that the readability of your scripts remains high.
AppleScript’s commands are often very close to plain English, making it a really easy language for beginners to pick up quickly.
The syntax for counting the windows is super straightforward: set (variable) to number of windows. AppleScript’s commands are often very close to plain English, making it a really easy language for beginners to pick up quickly.
The docText Variable
The odd part about the code above is that I’ve created an empty string variable, why would I do this? The reason will be more clear later, but basically our repeat will go through and add something to this every time and we simply needed a place to start. You can’t add something to a variable that doesn’t exist!
Step 4. First Repeat Block
To pull off our goal, we’re going to need two repeat blocks. The logic behind this is easier to wrap your mind around if you work from the inside out.
The logic behind this is easier to wrap your mind around if you work from the inside out.
Our goal is to grab the title and URL from a tab, then repeat that for every tab (repeat block). The trick is though that there could be more than one open window so we need to repeat that process for every window (repeat block).
Again, this thought process is inside out to how we need to work, so we’ll start with the “every window” repeat block.
tell application "Safari" --Variables set windowCount to number of windows set docText to "" --Repeat for Every Window repeat with x from 1 to windowCount set tabCount to number of tabs in window x end repeat end tell
A simple repeat block might say something like “repeat 5 times”, but we’ve done something a little more complicated here. The “repeat with” construct allows us to set up an intentionally generic variable (x) that will increment every time the repeat block is run.
For this to work, we need to tell our script the starting and stopping point for x as it cycles through its iterations. What I’ve done is told AppleScript to start x at 1, then increase it until it hits a value equal to the number of open windows, expressed as the variable that we set up before.
I used this construct because it makes our repeat block extremely versatile.
I used this construct because it makes our repeat block extremely versatile. If the script counts four windows, the repeat block will run four times, if it counts fifty, the block will run fifty times.
Counting the Tabs
Next, I basically repeated the process that we used before for the window count to set a variable to the number of tabs in the current window.
To pull this off though, I had to pull in the x variable. Remember that this variable will increase each time the repeat block is run. So the first time through, we’ll count the number of tabs in window one, the second time through, we’ll count the number of tabs in window two, etc.
Step 5. Second Repeat Block
Now that we have a repeat block set up that will go through every window, we need to insert another repeat block inside of that to look at every tab. The two together will be able to knock out every tab of every window.
While we’re here, we’ll also set up some variables for the next step.
tell application "Safari" --Variables set windowCount to number of windows set docText to "" --Repeat for Every Window repeat with x from 1 to windowCount set tabCount to number of tabs in window x --Repeat for Every Tab in Current Window repeat with y from 1 to tabCount --Get Tab Name & URL set tabName to name of tab y of window x set tabURL to URL of tab y of window x end repeat end repeat end tell
Repeat for Every Tab in Current Window
As you can see, this repeat block uses similar syntax to what we saw before. We already counted the number of tabs in the previous step so we set the repeat block to go from 1 to tabCount, using the generic incrementing variable y.
Grab Tab Name & URL
To finish off this step, we initiated two variables. The syntax of these is nearly identical, only one grabs the name of the current tab and the other grabs the URL. I found the specific syntax for these commands in the Safari AppleScript dictionary.
Notice that, to grab the name and URL of the current tab, we’re simply using the x and y variables that increment each time the repeat block is run. So the first time we grab the name and URL of tab one of window one, then tab two of window one, then maybe tab one of window two, etc.
Step 6. Fill docText String
Remember that empty docText variable that we created before? Now we’re going to fill it up with the text that we want for each entry in our list. Each time the repeat block runs, the title and URL of the current tab will be added to the variable using the HTML formatting that we outlined above.
The code for this is the most complicated syntax that we’ve come across so far, have a look below and then I’ll walk you through how it all works.
tell application "Safari" --Variables set windowCount to number of windows set docText to "" --Repeat for Every Window repeat with x from 1 to windowCount set tabCount to number of tabs in window x --Repeat for Every Tab in Current Window repeat with y from 1 to tabCount --Get Tab Name & URL set tabName to name of tab y of window x set tabURL to URL of tab y of window x set docText to docText & ¬ "<a href=" & "\"" & ¬ tabURL & "\">" & ¬ tabName & ¬ "</a>" & ¬ linefeed as string end repeat end repeat end tell
The first thing that you need to know here is that concatenation (putting stuff together) in AppleScript is handled via the ampersand. In the example above, I want add a bunch of different things to the variable each time, so I use the ampersand to connect all of the pieces together.
Adding to the Variable
Notice that I set the docText variable to “docText &…” a bunch of other stuff. Why would I set a variable to itself? What this is doing is telling AppleScript that I want to set the docText variable to everything already stored plus some other stuff.
By the end, this variable will contain the entire contents of what will be inserted into our TextEdit document.
This way, every time the repeat block is run through again, I’m adding to this variable and letting it grow rather than replacing its contents. By the end, this variable will contain the entire contents of what will be inserted into our TextEdit document.
Line Breaks and Linefeeds
There are two different forms of line breaks in the code above. The first is a series of superficial line breaks that I inserted into my code in an attempt to make it a little more readable. I wanted to make it clear that several different pieces are being added to the variable individually.
The “¬” symbol can be typed in AppleScript with Option+Return and indicates that the line break is merely a visual one and shouldn’t factor into how the code is run.
In reality though, all of this is a single line of code, which I would normally write as follows:
set docText to docText & "<a href=" & "\"" & tabURL & "\">" & tabName & "</a>" & linefeed as string
Notice near the end of this line of code is the word “linefeed”. Unlike the superficial line breaks above, this represents something that I actually want added to the variable. At the end of every line item, I insert a linefeed so that the next list item will begin on a new line.
Strings & Escaped Characters
The weird thing about the HTML text that we’re trying to insert is that it contains quotes, which are actually used by AppleScript to indicate the beginning and end of a string like so:
set myString to "Ladle rat rotten hut"
Here we initiated a variable and filled it with some text. The text is surrounded by quotes and tells AppleScript that the variable is of type string.
So if AppleScript uses quotes to surround strings, how do we store quotes as a part of a string? The answer is to “escape” the quote characters with a backslash like so:
set myString to "/"Ladle rat rotten hut/""
In the first example, the contents of the variable was as follows: Ladle rat rotten hut (stored without quotes). In the second example, the variable actually contained a set of quotes: “Ladle rat rotten hut”.
Putting it All Together
Given all of the information that we just learned, here are the individual pieces that are being inserted into our variable and stuck together.
- <a href=”
When all of these items are put together and the script is repeated for every tab in every window, we should have a list that is formatted exactly like the goal that we set out at the beginning.
<a href="www.siteurlone.com">Tab One Title Here</a> <a href="www.siteurltwo.com">Tab Two Title Here</a> <a href="www.siteurlthree.com">Tab Three Title Here</a>
Step 7. Create The Text Document
Now that we have all of this information stored into one convenient variable, it’s time to do something with it. In our case, we want to throw it into a text document.
To do this, create a new tell block for TextEdit like so:
tell application "Safari" --Variables set windowCount to number of windows set docText to "" --Repeat for Every Window repeat with x from 1 to windowCount set tabcount to number of tabs in window x --Repeat for Every Tab in Current Window repeat with y from 1 to tabcount --Get Tab Name & URL set tabName to name of tab y of window x set tabURL to URL of tab y of window x set docText to docText & "<a href=" & "\"" & tabURL & "\">" & tabName & "</a>" & linefeed as string end repeat end repeat end tell --Write Document Text tell application "TextEdit" activate make new document set the text of the front document to docText end tell
Inside the TextEdit tell block I’ve done three things: first, I told the application to “activate,” which simply brings it to the foreground so you can see the fruits of your labor.
Next, I told TextEdit to create a new document. Finally, I ended the script by setting the text of the new document to the variable that holds our list of links.
Try It Out
With that, our script is finished! If you’ve held on this long, congratulations, you’ve already written more AppleScript than the vast majority of Mac users and can consider yourself an elite member of the Mac nerd community.
To try out the script, make sure that you have Safari open with several tabs and several windows set to various websites. Once you have this set up, simply hit the play button in AppleScript to watch the magic happen.
Tip: Try saving out a version as an application so you can quickly run it whenever you need it.
Inevitably, many of you will reach this point and hit an error. Something went wrong, but what? Most often, the answer will be something as simple as a typo. Are you sure you entered the script exactly as it appears above? AppleScript typically attempts to point out the specific problem to you, follow its advice and retype the problematic line.
AppleScript typically attempts to point out the specific problem to you, follow its advice and retype the problematic line.
Unfortunately, it’s entirely possible that you entered everything correctly and are still receiving an error. Maybe there’s an issue with your version of Safari or perhaps the script doesn’t like the web pages you chose for some unknown reason. Try to change things up and see if you can pinpoint the problem.
Error handling is a fairly deep topic that I’ll cover in depth in a future article, for now take a look at our article on Advanced AppleScript Techniques for more information on how you can modify the script to take specific actions when an error is thrown.
I just walked you through the complete process of writing an AppleScript to generate a list of links from your open tabs in Safari. This should save you plenty of trouble on those big research projects.
Hopefully along the way you learned lots of great stuff about AppleScript and how to approach automating simple but repetitive tasks. If you have any questions, feel free to leave a comment below or hit us up on Twitter.