published on in AppleScript Hammerspoon

Closing Duplicate Tabs With AppleScript

⬅️ Previous post: Whither Perl

❤️ It's great to see you here! I'm currently available on evenings and weekends for consulting and freelance work. Let's chat about how I can help you achieve your goals.



tab

"tab can" by lokate366 is licensed under CC BY-SA 2.0

I have a browser tab problem. I open a lot of tabs in Chrome. I’m not great about closing them. To help me with my tab problem, I use Clutter Free. It prevents duplicate tabs from being opened. This works really well, but I can’t use this extension at $work.

At $work, I’m in an environment where I can only install Chrome extensions which have been approved by the org administrator. I could ask to get a new extension approved. It’s not a particularly onerous process, but I wondered if I could just script something to run on my Mac. That would allow me to solve the problem without a 3rd party Chrome extension.

In the past, I have copy/pasted AppleScript, but I don’t think I’ve ever written any from scratch. Maybe say "You look marvelous". That’s probably the extent of it.

I did, however, know that AppleScript can cycle through tabs. So, I came up with a plan to write some AppleScript that could cycle through all of the Chrome tabs on my Mac and close the duplicates. This solution doesn’t prevent duplicates from being opened, but it does close them after the fact and that mostly gets me to the same place.

I came up with the following:

#!/usr/bin/osascript

say "starting tab cleanup"
set urls to {}

tell application "Google Chrome"
    activate

    repeat with theWindow in every window
        set toClose to {}
        set tabIndex to 1
        repeat with theTab in every tab of theWindow
            set tabIsDuplicate to false
            repeat with seen in urls
                if (seen as string = URL of theTab as string) then
                    copy tabIndex to the end of toClose
                    set tabIsDuplicate to true
                    exit repeat
                end if
            end repeat
            set tabIndex to tabIndex + 1

            if tabIsDuplicate is not true then
                copy URL of theTab as string to the end of urls
            end if

        end repeat

        set closing to reverse of toClose

        repeat with closeIndex in closing
            close tab closeIndex of theWindow
        end repeat
        set numberOfClosed to count of closing
        if numberOfClosed > 0 then
            say numberOfClosed
            say "tabs closed"
        end if
    end repeat
end tell
say "finished tab cleanup"

This brings Chrome to the forefront and cycles through each open Chrome window. For each window, it cycles through every tab. If the tab’s URL has already been seen, the script stores the tab number in a list of tabs we will need to close. Once all tabs in a window have been checked, we reverse the list of tab numbers that contain duplicate URLs and close them one by one. Then we move on to the next window and continue the process, until we run out of open windows.

You may ask yourself why we need to reverse the list of tab numbers before we close them? We do this because it makes it easier to avoid off-by-one errors. Let’s assume we need to close tabs 3, 5 and 7. Once we close tab 3, tabs 5 and 7 become tabs 4 and 6. We could handle this by decrementing those numbers, but if we just take the list in reverse this will never happen. For instance, with the 3, 5 and 7 scenario, when tab 7 has been closed, tabs 3 and 5 remain in the same position, so there is less state to keep track of.

Caveat Emptor

The script above will close tabs with extreme prejudice. So, if you’re doing something important in a duplicate tab or you wanted to close the first tab rather than the second, you’re out of luck. This suits my workflow fine, but maybe not yours.

Getting the Script

You can copy/paste the script directly from this post or download it from my dot files repo. It can be run directly at the command line. You can also run it via the Script Editor app, which comes with macOS. That’s handy for debugging.

Addendum

As a satisfied Hammerspoon user, I’ve added this script to my Hammerspoon config. That means I can now run my tab cleanup with a keyboard shortcut. In my case that’s caps lock + a. I won’t get into Hammerspoon configuration today, but my config file is on GitHub and large parts of it were stolen directly from Mark Fowler. It may be helpful to point out that I’m using Karabiner Elements to map caps lock to command+control+option+shift. That config is also on GitHub.