I love automation. Why spend 20 seconds doing a task when I can spend half an hour automating it?

For my research, I depend on Jupyter. Specifically, I run a private Jupyter server on a server at the University. To access it, I need to forward a port from the remote server to my Mac, which is just a single command.

ssh -NL 8081:localhost:8081 username@server.com

I also then “Bury” the session, which in iTerm2 simply hides the terminal completely. Buried sessions don’t appear in the dock, or in mission control.

For convenience, I have a snippet set up in iTerm which will input the forwarding command for me.

  • So, I hit ⌘-Space, type “iTerm”, hit enter.
  • Then, I press ⇧⌘/, type “SSH”, hit enter.
  • Finally, I hit ⇧⌘/ again, type “bury”, and hit enter again.

That is a total of 20 keypresses! Simply not good enough.

There must be a way I can make this happen more simply. Maybe Shortcuts? (Terrible name BTW) No, iTerm2 doesn’t support Shortcuts (which is a real shame!). Maybe AppleScript? Well, iTerm2 does have AppleScript support, but apparently it’s deprecated. However, that webpage mentions Python scripting? Wait, Python?.

iTerm2

iTerm2 has integrated Python scripting! The amount of elements exposed seems quite impressive, although admittedly I’ve only examined a little slither of it so far! Here’s a list of the objects it exposes:

  • Alert
  • App
  • Arrangement
  • Broadcast
  • Color
  • Color Presets
  • Custom Control Sequences
  • Connection
  • Focus
  • Keyboard
  • Life Cycle
  • Main Menu
  • Preferences
  • Profile
  • Prompt
  • Registration
  • Screen
  • Selection
  • Session
  • Status Bar
  • Tab
  • Tmux
  • Tool
  • Transaction
  • Utilities
  • Variables
  • Window

Although, I think from my little experience, there’s actually even more objects available that are returned from some functions. Anyways, I had a very simple plan: I needed to create a window, run a command, then bury that window. Thankfully, there are some example scripts I could dissect.

When you first create a script, iTerm asks what kind of script you would like:

  • Basic A script with no external dependencies, just iTerm2 and basic Python libraries

  • Full Environment A script with access to external Python libraries and dependencies.

Here, I clearly needed a Basic script.

Next, you are asked how you would like your script to run:

  • Simple A single, one shot script.

  • Long-Running Daemon A constantly running daemon script.

So I chose one shot.

Below is the script I ended up with:

import iterm2
# This script was created with the "basic" environment which does not support adding dependencies
# with pip.

async def main(connection):
    # Your code goes here. Here's a bit of example code that adds a tab to the current window:
    app = await iterm2.async_get_app(connection)
    await iterm2.Window.async_create(connection, command="ssh -NL 8081:localhost:8081 username@server.com")
    window = app.current_terminal_window

    sessions = window.current_tab.sessions
    if len(sessions) >= 1:
        sess = sessions[0]
        await sess.async_set_name("SSH Forwarding")
        await sess.async_set_buried(True)

iterm2.run_until_complete(main)

It’s fairly self explanatory (and perhaps not all good practice!).

First, I get access to the app. Next, I create a window with the port forwarding command as the login shell command. Then I get the current window (which should be the one I just created), then get a list of all the “sessions” in that window (which should only be one, as it’s just been made!). I rename the session and bury it, then finally I tell iTerm to run the function. Just like that, done!

All it took was a little bit of code, and the forwarding part is finished! Now I can open iTerm, go to the “Scripts” tab, and select the script.

But wait, that’s still quite a few commands. Lets look back at those commands from earlier

  • Launch iTerm - ⌘-Space, type “iTerm”, hit enter.
  • Use the SSH snippet - ⇧⌘/, type “SSH”, hit enter.
  • Bury the terminal ⇧⌘/ again, type “bury”, and hit enter again.
  • Use the SSH script - ⇧⌘/, type “SSH”, hit enter.

Surely we can do better…

Raycast

Instead of Spotlight, I use a program called “Raycast”. Raycast is basically a more extensible spotlight with a number of great extensions included by default and a built in store to get new extensions.

As part of that extensibility, I can add scripts as commands to Raycast. And, thankfully, you can execute the iTerm2 scripts you write from the command line (although, you need to be sure to use the Python environment that iTerm created for you).

So, in Raycast I added a script that tells iTerm to execute my script. I then added a shortcut to make it easier to execute quickly, ssh. Now, if I open Raycast and type “ssh” the command appears, and when I hit enter it executes the command!

Now, lets look back at the commands again:

  • Launch iTerm - ⌘-Space, type “iTerm” “ssh”, hit enter.
  • Use the SSH script - ⇧⌘/, type “SSH”, hit enter.

Conclusion

This was alot of work for a very silly reward, but it’s very possible that these skills will be useful in the future for automating other things; although I don’t currently have any ideas of anything else I could automate. Well, at least nothing that could be solved exclusively via the terminal.