Running tmux in Mac OS X Terminal

I’ve been a fan of screen for … a while now. But since I like being one of the cool kids, I’ve been using tmux for the past year or so. Last week, I noticed that every time I launch a new terminal, I wind up typing tmux attach-session. Let’s streamline, a little bit.

In Mac OS X’s Terminal.app, you can change the shell that it runs. Here’s how I did it:

  • Open Preferences, and choose the Settings tab.

  • Duplicate your existing settings (since sometimes you might not want tmux after all). Pick your default session (mine’s “Pro”) and select “Duplicate settings” from the tool menu at the bottom. Name the new settings “Tmux” or something along those lines.

  • In the shell tab for your settings, select “Run command” and enter /usr/local/bin/tmux attach-session. Deselect “Run inside shell” since you don’t really need to. Since you’re not running inside a shell, /usr/local/bin probably isn’t in your $PATH so you’ll need to specify the full path name. Of course, if your tmux binary lives somewhere other than /usr/local/bin you’ll need to change the path.

  • If you’ve selected “Only if there are processes other than” for “Prompt before closing”, then you’ll probably want to add tmux to that list.

  • In the “Window” tab, I set “Scrollback” to limit the number of rows to ’0′, since tmux provides scroll back, and the Terminal one isn’t terribly useful when tmux is running inside it.

  • Make sure your Tmux session is set as the default one by clicking the “Default” button at the bottom of the settings lists while it’s selected.

That’s it. Close your existing terminal sessions and launch a new one. You should be launched into (one of) your existing tmux sessions. If tmux wasn’t already running, then this assumes that your ~/.tmux.conf sets up at least one session (which I think it required anyway). If you’ve got more than one tmux session running, I’ve no idea which one, offhand, it’ll choose, but you can always switch to the one you’re looking for with C-a s. (You have rebound the prefix to C-a, right?)

There’s (at least) one time where you don’t want tmux as your shell. That’s when you’re attempting to interact with launchd. I suspect it’s to do with launchd checking that you’re a child process as part of its permissions when you’re asking it to do stuff, where tmux works by detaching itself from its parent process so it’s not killed when the parent is. (Total guess, BTW.) Still, when you want to use launchctl, you’ll need to do it somewhere other than a tmux session. In Terminal, choose the Shell menu, choose “New Window” (or “New Tab”) and select one of the other settings profiles.

Using the shell within irb

A couple of weeks ago, I had a little diversion, playing around with the shell bits and pieces in Ruby core. And it sounded like it could be quite handy for when you’re in that IRB session and don’t want to quit/reload (or switch to another terminal, I guess). So I added a few widgets to by ~/.irbrc to make it work well for me. You can find the relevant parts in shell_from_irb.rb. Probably the easiest thing to do is copy and paste the contents into your own ~/.irbrc.

So, how do you use it? From the irb shell:

irb(main):003:0> shell.echo 'hello world'
=> hello world

Or, something more useful which will return you an array of files you’ve modified since your last commit to subversion:

irb(main):016:0> shell.svn(:stat).grep(/^M/).collect do |line|
irb(main):017:1*     line.gsub(/^M */, '').chomp
irb(main):018:1>   end
=> ["app/helpers/application_helper.rb",
     "app/controllers/application.rb",
     "config/photography_config.rb",
     "public/javascripts/lightbox.js",
     "public/stylesheets/lightbox.css"]

Oh, and since all the shell commands in your path are now effectively methods, if you have readline and completion switched on, you can do what you’d usually do in the shell:

irb(main):019:0> shell.sv[tab][tab]
shell.svk             shell.svnadmin        shell.svnserve
shell.svm             shell.svndiffshim_py  shell.svnversion
shell.svn             shell.svndumpfilter
shell.svn_backup_sh   shell.svnlook
irb(main):019:0> shell.sv

I’ve made a few modifications to the default behaviour of the shell widget in Ruby core. I’m installing the commands with no prefix, so that it’s easier to use. By default, however, that would override the implementation of a function with the latest one found. The default behaviour of the shell is to use the first match, so I’ve fiddled things so that it also uses the first match. This has the added benefit that it doesn’t override Shell‘s internal implemtation of things like pwd which otherwise causes the thing to completely break (since it’s also used internally). That’s what FixAddDelegateCommandToShell does, for those of you playing along at home.

I’ve also allowed you to specify arguments to system commands as symbols. It feels more natural to me to type shell.svn :stat than shell.svn 'stat'. I guess YMMV.

Lastly, it’s all wrapped up in a singleton widget which lazily loads the shell functionality. That makes it available any time, but pushes the time hit (where it scans $PATH and creates all those methods for each command) to the first use, rather than when you launch irb.

So there you go. I’ve found it useful, so I thought I’d share.