The Terminal application has been steadily improving with every major release of Mac OS X. A new feature called Window Groups was recently added which saves the state, tabs, and locations of all terminal windows at a set point in time. I’ve personally found this to be a very helpful feature with just one caveat: It requires frequent upkeep when working with multiple projects.
The Window Group Upkeep Problem
I currently work on nine Ruby on Rails applications that are all tied together via a single sign-on server and signed API requests – think of it like an ecosystem of decoupled apps. When I’m developing within this ecosystem the task of trying to keep track of all the activity going on between these apps is a major pain point.
Before this post I had originally tried to solve this control and monitoring problem by running three Terminal.app Windows, each dedicated to running a tab (per application) for …
$ rails console
$ tail -f -n 100 log/development.log
- and a Shell for running rake tasks, tests, and other commands.
This solution started out simply enough but quickly ballooned as more apps were added to the ecosystem.
The pain of this solution was just too much to ignore after the addition of the 9th app resulted in 27 tabs spread across 3 terminal windows. It was clear that the simple solution I has come up with would no longer scale – I reached out for some advice.
Consolidating The Mess Of Windows & Tabs
I knew there had to be a better way to manage these growing number of applications – surely others experienced this problem?! I switched over to the Indy.rb Meetup Group’s IRC channel and asked the open question of how everyone had been going about their terminal management. Soon thereafter many people were coming to the table with various suggestions, techniques, and solutions to how they’ve address the issue.
The majority of opinions fell on the solution that involved using just a single Terminal.app window with one tab per application; each tab would then be running a tmux session that would group the application’s operations and information together.
Being juuuuust a little bit late to the tmux party (it was released in 2009) I finally decided to “get my tmux on” and dove right in. After spending a couple of hours looking at documentation and messing around with config files I ended up consolidating my original setup of “3 windows + (3 tabs * App)” into “1 window + (1 tmux tab * App)” – finally some sanity!
Note: I’ll discuss my tmux setup and configuration in another post soon!
Scriptable Terminal.app Tabs
With all of the aforementioned advancements to the Max OX Terminal.app I still found controlling it from the command line to be quite a hassle. I had a great new solution for consolidating my development environment but it still required me to do some manual setup. I wanted to find a way to launch all of my applications’ tmux sessions from one Terminal.app window into separate tabs.
I needed a way to script…
- Creation of a new tab within the same Terminal window.
- Changing the title of each new tab to reflect the application’s name.
- Executing arbitrary commands after each tab was created.
After using some serious black-belt level Google-Fu, and sifting through many StackOverflow articles, I managed to hack together a handy little shell function that allowed me to do just that.
Below is a sample launch_dev.sh
executable shell script that contains a new_tab()
function, and some examples of its usage…
#!/bin/bash # File: ~/launch_dev.sh function new_tab() { TAB_NAME=$1 COMMAND=$2 osascript \ -e "tell application \"Terminal\"" \ -e "tell application \"System Events\" to keystroke \"t\" using {command down}" \ -e "do script \"printf '\\\e]1;$TAB_NAME\\\a'; $COMMAND\" in front window" \ -e "end tell" > /dev/null } # Let's make a new tab called "My Projects", change to the # projects directory, and list the contents. new_tab "My Projects" "cd ~/projects; ls -l" # Let's also open up our deployment scripts directory # too -- that would also be handy! new_tab "Deployment Scripts" "cd ~/scripts/deployment"
What Is The new_tab()
Function Doing?
Basically, it’s utilizing a ton of wrapping. Let’s start from the top down…
- I’m creating a
new_tab(tab_name, command)
shell function which primarily uses theosascript
command to execute custom AppleScript code. - Apple scripting instructs the Terminal application to open, or become focused.
- Apple scripting simulates a key press of ⌘+T – the shortcut for opening a new tab in Terminal.app
- Apple scripting continues by executing a
do script
command, which in-turn executes the\e]1;$TAB_NAME\a
shell command – setting the newly created tab’s title.- Side Note: The
\e]1;
and\a
snippets are interesting aspects of the shell that I knew nothing about prior to embarking on this endeavor. I originally found the technique in a StackOverflow article, but no explanation was given on why it worked to set the title. I would encourage you to check out the “Dude, what’s up with your prompt?” blog post by Vince Aggrippino to find out the answer.
- Side Note: The
- The previous
\e]1;$TAB_NAME\a
command is chained with a semi-colon, and then followed by a$COMMAND
variable that gets evaluated and executed in the new tab as well. - Lastly, any output from the original call to
osascript
is redirected to/dev/null
.
Cool huh?
Summary
This ability to open, name, and command new terminal tabs through a simple shell function has vastly changed the way I operate in my current multi-application ecosystem. When I want to setup my development environment I can simply run my ~/.launch_dev.sh
shell script and have multiple tabs open, name themselves properly, and attach themselves to tmux sessions.
It’s important to keep in mind that when it comes to the topic of Development Environments it really is a case of to each their own. With this shell function one can now easily configure and launch their development environment’s tabs all from a simple script. This in turn opens up countless possibilities for anyone looking to gain better control over their Terminal.
This function is not “The Solution” to building and managing development environments. Rather, it’s simply a tool to help create solutions.
If you do end up using this code for anything please come back and comment on your successes – I’d love to hear them :)
Enjoy!
Environment
The environment and tools used at the time of this post are as follows:
- Max OS X Lion (Version 10.7.5)
- Terminal.app (Version 2.2.3)
License
This code is released under the New-BSD License.