tmux
is a terminal multiplexer. If
you’re familiar with screen
, you’ll have an idea what tmux can do. This is
not a tutorial for how to use tmux as there are already plenty of those. It’s a
description of how I use tmux in case that’s of any use to anyone else.
I also don’t include or link to my tmux.conf
verbatim, as I’m not a fan of
canned configurations. I think it’s much more productive to understand the
configuration of the tool yourself, and create a configuration that’s exactly
what you need. Borrowing snippets are fine, but you should understand what
you’re pasting in.
Some history
You can skip this section if you don’t care. It’s about how I ended up using tmux the way I do.
Some years ago, the only thing I used either program for was running my IRC client detached from the terminal, and maybe having another shell open on the same host without having to start another connection. Then, after changing jobs, I ended up sitting next to a guy who used screen all the time on his local machine. This meant he had only one terminal window open (usually fullscreen, I seem to remember) with many (what tmux calls) windows in it. “Meh,” I thought, “I’m doing that with tabs and split windows in my terminal. But he was also doing stuff I’d never really looked into with screen, like using it to scroll back though long output, and copy/paste.
At the time I was using tmux, because screen had been doing this weird thing where it would block the tty while detached, so my IRC client would time out, and spring to life when I reattached to it. I configured tmux to use the same prefix key (⌃ ctrla) and left it as that.
A little while later, I wanted to be able to ssh into my work laptop and share a terminal session from it, and in realising tmux would allow me to do that - multiple clients can connect to the same set of windows - I started using it in the same way my coworker did, and everything ran inside tmux by default.
My tmux workflow
In the examples below, Prefix stands for ⌃ ctrla because of my configuration. By default, it’s ⌃ ctrlb. Since I also remap ⇪ caps lock to ⌃ ctrl this prefix is super-easy to hit.
Sessions
On local machines I tend to have three sessions running: one for a terminal
window, one for a fullscreen terminal window, and one connected to remotely. On
remote machines, I tend to just have one session. The three sessions are
usually named local
, fullscreen
and remote
. I don’t use any session
manager like tmuxp or
tmuxinator as my sessions aren’t
predictable. However, as described below one of the plugins I use saves my
session state periodically, although this is so that my session and all its
windows can be resumed after restarting the host (or maybe upgrading tmux).
Creating multiple shared sessions
The goal here is to have multiple sessions which all share the same set of windows.
Start by creating the first session:
% tmux new-session -s local
This will create a new session called local
. (You can use whatever name you want.) You can make some windows with some content here if you want to prove that the subsequent sessions are sharing windows with this one.
In another terminal, make another session called fullscreen
linked to this one:
% tmux new-session -s fullscreen -t local
This command creates a session called fullscreen
which is in the group local
. The way this works is that -t
specifies a session group, which can be the name of an existing session. In this case, a group is created named after the target session. This is visible in the output of list-sessions
:
% tmux list-sessions
fullscreen: 4 windows (created Wed Aug 16 20:30:22 2017) [204x66] (group local) (attached)
local: 4 windows (created Wed Aug 16 20:30:05 2017) [136x43] (group local) (attached)
If you specify something for -t
which is neither an existing group name nor an existing session, it creates a new group with that name and puts your new session in it. I don’t generally bother, so my group always ends up called local
.
To attach one of these sessions (for example if you quit your terminal), use
% tmux attach-session -t session_name
Note that you can attach multiple clients to the same session, which results in
all of them sharing the same view. If you change windows on one client, all the
others attached to that session will show the same. Additionally, if one of the
clients is smaller than the others, the others will shrink their view to match
that one. To avoid this happening if you have a client running elsewhere which
is attached to the session you want, add the -d
option to the
attach-session
command to make it detach elsewhere.
If you want to rename a session, Prefix$. You can jump between sessions in a client with Prefix( and ).
This is all different to creating a separate server, which you do using the
-S
option to tmux. The result of using this to create different “sessions” is
that each will be fully independent, with its own windows. If you want to do
this instead of (or maybe as well as) the technique I’m describing, you can do
it by omitting the -t
option when creating a new session. This has the
benefit of meaning all your sessions show up in one place (i.e. inside one
server).
Windows
As above, my sessions aren’t predictable, and I tend to just create windows
when I need a new shell to do something. If I’m working on something particular
for a while then I’ll name the window Prefix: rename-window
.
Otherwise, windows are named automatically by my shell for the current
directory and running process. I also make use of the window list Prefixw and find window commands Prefixf.
When you have multiple sessions sharing a group of windows, if two clients both
select the same window then tmux will shrink the window to the smallest
client’s size. Bigger clients will see their extra space filled in with dots.
If the aggressive-resize
option is enabled (which it is by the
tmux-sensible
plugin, see below) then tmux will try to size the window up
when it can, which means that selecting a different window in the smaller
client will free up the larger client to resize the window to the full size.
To move a window between session (groups), for example if it was created in the
wrong session group, you can bring it to your current session (group) with Prefix: move-window -s source-session:0
where 0
is the window id.
This moves the window to the next free slot in the current session. You can
also use link-window
to have the window show up in multiple sessions
simultaneously.
Panes
I am a heavy user of panes (split windows) as I find them useful for things such as having a terminal or two alongside files I’m editing, or documentation. While my preferred editor (neovim) supports terminals in windows, I find I get on better with separating my editor and shell into tmux panes. I use a tmux/vim plugin which lets me use the same keystroke ⌥ alth, j, k, l to navigate vim split windows and tmux panes as though they were all part of a single unified layout.
Another pane feature I use a lot is zooming Prefixz, which makes the current pane temporarily use the whole window. (Its equivalent in iTerm2 is ⌘ cmd⇧ shift⏎ return.)
Status bar
Status bars seem to be something people feel very strongly about, so this section is likely to be a YMMV thing. I like mine minimal but informative. The items on my status bar are:
- session name (left aligned). This goes blue when I’ve pressed the Prefix
- window list, where each item is the window number, flags, and title (by default,
process:cwd
) (left aligned). The current window is white-on-blue; the previous window is blue on black, and other windows are white on black. If there’s been a bell in another window, it goes red. If it’s been monitored for activity or idleness, and triggers, it goes green. - system load, from my tmux-loadavg plugin (right aligned). This changes from green to orange to red as the load increases.
- short hostname (right aligned)
The formatting and style is similar to that of powerline and indeed I used powerline for a while, but found a few issues with it:
- it’s not very straightforward to customise (or at least, it didn’t seem it)
- the pointy widgets separating each section are nice eye candy, but waste some characters which are better used for displaying useful information
- I had a lot of problems on OS X with it causing very high system load. This was not powerline’s fault but more it, or tmux, exposing an issue in OS X/macOS - possibly related to notifyd. It was difficult to diagnose, but it had a measurable impact on my system; most notably it sucked so much CPU that the already-heavy Google Hangouts was basically unusable
- it seemed wasteful to have a whole daemon running just to render a line of text
- it’s another thing to install and manage
To that end, I switched from using powerline to using static tmux configuration which used the same style (minus the pointy characters). I dropped the date/time widget as this isn’t something worth wasting those characters on. I have many other ways to find out the date, and if I want to find out the time while using a fullscreen terminal (so I have no menu bar) then I use tmux’s clock display Prefixt.
The load average widget was replaced by writing my own as a tmux plugin, and it is a single bash script which uses an awk or two to help format text. I find the hostname display useful despite the fact my shell displays it in the prompt if I’m connected to a remote machine, as often the content of my tmux window is a fullscreen editor.
Plugins
I use the tpm plugin manager, with these plugins configured:
- tmux-sensible: basic tmux settings everyone can agree on
- tmux-pane-control: standard pane key-bindings for tmux
- tmux-yank: integration with system clipboard
- tmux-resurrect and tmux-continuum: frequently saves the state of the tmux session and automatically restores it
- tmux-loadavg: my plugin to show the load average in the status bar
Additional configuration
Some bits from my configuration which aren’t described above.
Binds
This section switches Prefix to ⌃ ctrla, and adds a couple
of other screen
isms because of my muscle memory.
Normally Prefix, prompts for a new window name, and this does the
same but then locks the window title from being overwritten with the
process:cwd
name by the shell.
Settings
This may not be required any more, but was a workaround for the issue where
notifyd
would cause high CPU usage with powerline. The status bar is still
updated as needed, and the only thing which this affects is the load average,
so I haven’t bothered removing it yet.
By default, the maximum length for the leftmost block on the status bar, which
will contain the session name, is too short for the word fullscreen
so this
fixes it.
Supporting cast
zsh
zsh has excellent tab completion for tmux. Additionally,
since I use it by default, I have this at the end of my zshrc
:
This shows me any tmux sessions which are running but not attached when I start a shell:
==> Available tmux sessions: fullscreen remote
~ %
vim
I use a couple of vim plugins (via vim-plug) to help with tmux:
The first allows vim to receive focus events from tmux, so that switching
to/from windows/panes with vim in triggers autocmd
s for appropriate events.
It’s also required for the next item.
The second shares the vim "
buffer with tmux’s clipboard, making for easier
pasting of large amounts of text into vim, or out of it.
The third provides the integrated pane navigation with tmux, and to go with it I use the following configuration:
The second setting is crucial for me, as without it, trying to move out vim while vim is in a zoomed pane will unzoom it, which more often than not is not what I meant.
The if
is needed so that the bindings still work to navigate panes if the
plugin is loaded, which it won’t be if the $TMUX
environment variable isn’t
set, thanks to the Cond()
part of my plugin definitions.
iTerm2
iTerm2 has tmux integration so that your windows and panes in tmux are rendered as iTerm windows and panes. However, I found that - cool as this was - it threw off my workflow. I much prefer a single window or tab with my tmux session in it. Maybe more tabs for sessions on other hosts.
Tips and tricks
A grab bag of useful or cool things I don’t use day to day, but have found on my tmux journey.
Buffer lists
Hopefully you know about copy-mode Prefix[. Things which you copy go into the tmux paste buffer, but there’s a history of buffers so you can recall previously copied stuff! Prefix= will open a list for you to scroll and select, and will paste the entry you choose. To view them without pasting, Prefix#.
Windows and panes
If you have a pane you want to move into a window of its own, Prefix!. To move a window into being a pane of an existing window. Prefix: join-pane -s WINDOW
. You can also ‘mark’ the pane to move with
select-pane -m
, and then use join-pane
by itself in the target.
To move panes around in a window, for example if you’ve selected a canned layout (Prefixoption1 etc) and want them in a different order, Prefix{ and }.
You can make the current window show the time (only) with Prefixt, which I use in place of having the time in my status bar.
To get a tree view of all your sessions and windows, Prefixs. Use → to expand a session into its list of windows.
Help
Do not underestimate the man page for tmux. It contains everything you need to
know, although it does make for dry reading. You can also bring up a list of
all the current keystrokes with Prefix?, and then search in that
list with / like less
.
Conclusion
I hope something here has been of use to you, and that you can find tmux a useful tool. Happy tmuxing!