A small usage guide to tmux: a competent and lightweight GNU Screen replacement. Written from a Debian perspective, but mostly independent of platform.

Install tmux

sudo aptitude install tmux

First run

tmux

Now you're doing it. A shell is auto-spawned. From GNU Screen you are used to Ctrl+a to execute special commands. With tmux you should get used to Ctrl+b. Another small difference is that you need to release Ctrl before executing the function key, i.e. Ctrl+b, then d to detach, instead of the GNU Screen friendly Ctrl+a d without releasing Ctrl.

To

  • detach,
  • create a new window,
  • switch window,

etc., the default keys are the same as in GNU Screen, so the transition should mostly be painless.

Configuration

My ~/.tmux.conf follows – teaching by example. Each configuration section is followed by a description.

set-option -g default-terminal screen-256color

This is a good termcap entry which makes Vim behave nicely. The screen-256color comes with the ncurses-term package, so make sure it is installed. If you run a 256 color compatible terminal, this should give you nice colors. Test this with e.g. this script.

#!/usr/bin/perl
# Author: Todd Larason <jtl@molehill.org>
# $XFree86: xc/programs/xterm/vttests/256colors2.pl,v 1.1 1999/07/11 08:49:54 dawes Exp $

# use the resources for colors 0-15 - usually more-or-less a
# reproduction of the standard ANSI colors, but possibly more
# pleasing shades

# colors 16-231 are a 6x6x6 color cube
for ($red = 0; $red < 6; $red++) {
    for ($green = 0; $green < 6; $green++) {
        for ($blue = 0; $blue < 6; $blue++) {
            printf("\x1b]4;%d;rgb:%2.2x/%2.2x/%2.2x\x1b\\",
               16 + ($red * 36) + ($green * 6) + $blue,
               int ($red * 42.5),
               int ($green * 42.5),
               int ($blue * 42.5));
        }
    }
}

# colors 232-255 are a grayscale ramp, intentionally leaving out
# black and white
for ($gray = 0; $gray < 24; $gray++) {
    $level = ($gray * 10) + 8;
    printf("\x1b]4;%d;rgb:%2.2x/%2.2x/%2.2x\x1b\\",
       232 + $gray, $level, $level, $level);
}


# display the colors

# first the system ones:
print "System colors:\n";
for ($color = 0; $color < 8; $color++) {
    print "\x1b[48;5;${color}m  ";
}
print "\x1b[0m\n";
for ($color = 8; $color < 16; $color++) {
    print "\x1b[48;5;${color}m  ";
}
print "\x1b[0m\n\n";

# now the color cube
print "Color cube, 6x6x6:\n";
for ($green = 0; $green < 6; $green++) {
    for ($red = 0; $red < 6; $red++) {
        for ($blue = 0; $blue < 6; $blue++) {
            $color = 16 + ($red * 36) + ($green * 6) + $blue;
            print "\x1b[48;5;${color}m  ";
        }
        print "\x1b[0m ";
    }
    print "\n";
}


# now the grayscale ramp
print "Grayscale ramp:\n";
for ($color = 232; $color < 256; $color++) {
    print "\x1b[48;5;${color}m  ";
}
print "\x1b[0m\n";

hide script

set-window-option -g xterm-keys on

This makes my Vim commands work consistently in combinations of rxvt-unicode, tmux, GNU Screen and SSH from certain other machines.

set-option -g history-limit 4095

Increasing the line history will give a minuscule performance hit for a very convenient functionality.

unbind |
bind | split-window -h
unbind -
bind - split-window -v

More intuitive key bindings to split windows.

# Ctrl+Space cycles panes cl  w/out prefix key
# Ctrl+a     cycles panes ccl w/out prefix key
#(why oh why can't we bind e.g.
#    C-S-Space
#    C-Tab
#    C-S-Tab
#    C-S-a (or C-A if you want)
#separately?)
bind -n C-Space select-pane -t :.+
bind -n C-a select-pane -t :.-

Ctrl+Space cycles windows. Very useful. My despairing comments shown above concerns the fact that it is not possible to bind e.g. Ctrl+Shift+Space separately from Ctrl+Space, for some intricate terminal reason.

# Default binding enables repetition on arrow keys. I don't like this.
bind Up select-pane -U
bind Down select-pane -D
bind Left select-pane -L
bind Right select-pane -R

The problem with repetition is that it implies a timeout. When I switched to another window and directly wanted to initiate an arrow command (which happened very often, as it turned out), tmux captured this first keystroke and sent me to another window.

Needless to say, man tmux contains information on more configuration directives.

Executing tmux commands

This is a fundamental design choice in tmux. All configuration, keyboard shortcuts, etc., are represented by descriptive commands that can be executed either from within the session, or via the command line. For example, to create a new window in the named session log, naming the window duplicity and executing a command, run:

tmux new-window -t log:1 -n 'duplicity' '~/bin/duplicity-backup-logfollow'

in the command line. -t targets the specified session. :1 distinguishes that we want a second window (:0 being the first). -n names the window. The last string is a command to execute (if left blank, it will spawn a shell).

That was a specific example meant to illustrate that every tmux interaction can be done through CLI. This is very script friendly, as all CLI users will immediately recognize.

These commands can also be given from within a tmux session. Ctrl+b : opens a command line that takes tmux commands interactively. Every action is representable by such a command. man tmux lists them all.

Detach and attach sockets and sessions

Detach a running session using Ctrl+b d. Sessions run under specific sockets. Sockets are stored in /tmp/tmux-1000/, where 1000 represents the uid of the user. The default socket is used unless otherwise stated (aptly named /tmp/tmux-1000/default). To specify another socket, use tmux -L socketname. This is not needed in non-exotic situations, since the default socket can hold many named sessions, which is more memory-friendly than multiple sockets. Use named sessions.

To name a session e.g. "log", either

  • execute Ctrl+b $ followed by log from within the session,
  • execute Ctrl+b : from within the session to enter command mode, followed by rename-session log, or
  • run tmux rename-session -t oldsessionname log on the command line.

The session name is by default seen in the lower left of the session. A list of sessions is obtained by running tmux list-sessions.

To attach to the session "log", run

tmux attach-session -t log

By default, many clients can attach to the same session and share input and output. This is sweet. You can leave a session attached, SSH in to attach remotely, and the terminal text will even re-flow to fit the smallest attached terminal. Shared sessions (and of course remote detachment) are supported in GNU Screen, but requires a bit more effort, and tmux just works (tm).

Splitting

This is nicely done in tmux. It looks great and has a very low performance penalty, which can be substantial with some other splitting solutions.

To split, see my above posted config. By now it should be clear how to execute a command, either in-session or in command line, to split a window. The default keyboard shortcuts are Ctrl+b " and Ctrl+b % for vertical and horizontal splitting respectively. I think my shortcuts are more intuitive, ergo their existence in my config file.

Sample startup script

This is a sample log aggregator that I execute at startup:

#!/bin/sh
WNAME="log"
tmux new-session -d -s ${WNAME} -x 208 -y 67 'tail -f .uploader/ncftp/log | grep -E "(Succeeded|Couldn.t open)"'
tmux rename-window -t ${WNAME}:0 'p2p'
tmux split-window -v -t ${WNAME}:0 'tail -f ~/.uploader/rssdler/downloads.log'
#tmux split-window -h -t ${WNAME}:0 'tail -f ~/.uploader/microdc2/log'
tmux split-window -h -t ${WNAME}:0 'tail -f ~/.uploader/ncdc/logs/transfers.log'
tmux select-pane -D -t ${WNAME}:0
tmux split-window -h -t ${WNAME}:0 'tail -f ~/.uploader/scripts/torrent_finished.log'

tmux new-window -t ${WNAME}:1 -n 'duplicity' '~/bin/duplicity-backup-logfollow'
tmux new-window -t ${WNAME}:2 -n 'error.log' 'sudo tail -F /var/log/apache2/error.log'
tmux new-window -t ${WNAME}:3 -n 'access.log' 'sudo tail -F /var/log/apache2/access.log'

tmux set-option -g -t ${WNAME} monitor-activity on

Ignoring the exact commands that end all (new-(window|session)|split-window) lines, all parts should be easily understandable through either direct inspection or man tmux. This sets up a named session with four named windows. The first window is split into four named panes. monitor-activity on makes the tabs highlight on activity.

Push keystrokes into session

This is not a common task, but I use it in an application, so I present it.

tmux send-keys -t ncdc M-a-2 "/say Nytt: ${RELEASE}" C-m "/refresh" C-m

This sends the key strokes specified to the session named ncdc. M-a-2 is more familiar to some described as Alt+2, the strings within quotes are regular strings, and C-m is a carriage return (=the enter button).

GNU Screen would handle this similarly as

screen -S ncdc -X eval 'stuff "/say Nytt: '"${RELEASE}"'\015/refresh\015"'

tmux just seems... simpler. But both are functional.

Conclusions

tmux, mainly in comparison with GNU Screen,

  • looks nice,
  • is memory friendly,
  • handles split windows well,
  • is very scriptable,
  • is surprisingly bug free (not least when I compare to my experience with the much more mature GNU Screen)

and more. GNU Screen is the de facto standard in the area, and thus is still very useful to know, but at home, I prefere to use tmux.

Also, if it's only the detach functionality one is after: look at dtach which does this, and only this. You won't beat its resource timidity.