Shells All The Way Down

Install tmux

In this chapter we will be installing, configuring, and learning how to use tmux. tmux is a terminal multiplexer, which is just a fancy way of saying it is a program that allows you to run multiple shells simultaneously. You can detach from a tmux session, do some work in the host shell, the reattach to the tmux session to continue working. You can have many different named tmux sessions for different purposes and pop into and out of those terminal sessions as needed.

You might wonder why you should bother using tmux since any worthwhile Terminal provides the same capabilities. You can open new tabs to run multiple shells, you can split the Terminal window into multiple panes, and so on. One good reason is portability. Most Terminals only work on the platform they were built for. You can’t use iTerm2 on Ubuntu, just as you can’t use Terminator on macOS. If you wanted to make use of these features on multiple platforms you would need to learn and remember the shortcuts for both platforms.

Perhaps the most important reason, for our purposes, is that you can write tmux scripts to setup tmux sessions. Imagine you are working on a web site. Your development environment will include a local HTTP server to serve the web pages, a local database of some sort, perhaps some background job processing service, and so on. Each of these services needs to be up and running so you can write and test your code effectively. With tmux you can write a short script that will start all of the necessary services, along with a window for your editor, with a single command.

tmux is one of those tools you really need to see working to understand how helpful it can be, so lets get going and install it now. This book will be using features from tmux 2.5, so verify that the version of tmux is at least 2.5 or higher.

Ubuntu

As usual installing tmux on Ubuntu is pretty straightforward. Simply use Apt to install it.

$ sudo apt install tmux

Once that is complete verify tmux is installed.

$ tmux -V
  tmux 2.6

macOS

Installing tmux on macOS is also simple. You should have Homebrew installed already from previous sections of this book. Use it to install tmux as shown below.

$ brew install tmux

Once that command is done verify that tmux is installed.

$ tmux -V
  tmux 2.8

Update the Dotfiles Project

Before we start using tmux lets set up a tmux configuration file and add it to the dotfiles project. By default tmux will look for configuration settings in two places. First in /etc/tmux.conf for system wide tmux settings then in ~/.tmux.conf for user specific settings. If neither of these files exist it simply starts with default settings.

We are going to follow the same pattern we did with the Z Shell configuration files. We’ll make a directory in our dotfiles project named tmux and place our personalized .tmux.conf file there. Then we will make a symbolic link from ~/.tmux.conf to the actual file in the dotfiles project.

$ mkdir ~/dotfiles/tmux
  $ touch ~/dotfiles/tmux/.tmux.conf
  $ ln -s ~/dotfiles/tmux/.tmux.conf ~/.tmux.conf

Do not commit and push those changes yet. We will be making some simple configuration changes next that will make using tmux quite a bit easier.

tmux Configuration

Before I show you how tmux works there are some simple configuration changes that should be made. These changes will make the overall experience of using tmux much more enjoyable.

Prefix Key

tmux uses a prefix key sequence for most of its commands. This is necessary to separate key sequences that are meant for the underlying shell from key sequences that are meant for tmux. By default the prefix key sequence is <C-b>. If you are touch typing that key sequence requires both hands. It is a little bit clunky to use, especially if you are sending many commands to tmux.

Fortunately tmux allows you to change they prefix key sequence. The sequence we will use is <C-a>. If you followed my advice in the Getting Started chapter and remapped your <Capslock> key to <Ctrl> then <C-a> is incredibly easy to type. The two keys will be right next to each other. To make this change open the tmux configuration file you created previously and enter the following lines.

~/dotfiles/tmux/.tmux.conf
  # Set the prefix key sequence to Ctrl-a
  set -g prefix C-a
  unbind C-b

You can add comments to your tmux configuration file by starting the comment with a hash character (#). The -g switch tells tmux to set the prefix key globally, for all tmux sessions you create. We also unbind the <C-b> key sequence so it can potentially be used for some other purpose.

You may remember from the Z Shell chapter that <C-a> is a line editing command when Emacs Mode is set. Did we just override that command? Yes and no. Yes, when you are in a tmux session <C-a> will be captured by tmux and not sent to the shell. We can add a setting that allows us to pass it through though. Add the following lines below the prefix key changes.

~/dotfiles/tmux/.tmux.conf
  # Send Ctrl-a through to the shell when pressed twice.
  bind C-a send-prefix

This will allow you to still use <C-a> in the shell to move to the start of the command line. The only downside is you now have to press it twice.

For the rest of the book I will refer to the prefix key sequence as <Prefix>. Just translate that to <C-a>, or the key sequence you used if you used something different.

Command Delay

tmux introduces a short delay when sending key commands. This can interfere with other programs that we will be using, so we will remove the delay. After the prefix configuration changes above add this to change the delay.

~/dotfiles/tmux/.tmux.conf
  # Remove the prefix command delay
  set -s escape-time 1

Mouse Mode

I talk a lot in this book about keeping your hands on the home row and how much faster that will make you as a developer, so why am I having you set tmux mouse mode on? Well, there are times when it is just handy to have some mouse capabilities. Mouse mode in tmux will allow you to scroll through the shell output using the mouse scroll wheel, select panes with the mouse, and other conveniences. It can also be handy to have Mouse mode enabled during your transition phase. If you get stuck or forget the key sequence for scrolling a tmux session you can grab your mouse and still get it done. Add the following lines after the prefix delay lines.

~/dotfiles/tmux/.tmux.conf
  # Turn on Mouse Mode
  set -g mouse on

We use the -g switch again to make sure the setting is available in all of our tmux sessions. The setting defaults to off, so, if at a later date you decide you no longer need Mouse mode you can simply delete these lines from your configuration.

Make It Colorful

We made sure the terminal we are using can display true color way back in the Getting Started section. We also need to make sure the tmux is set to use true color. Add the following lines to your tmux configuration file.

~/dotfiles/tmux/.tmux.conf
  # Enable true color support.
  set-option -ga terminal-overrides ",xterm-256color:Tc"
  # Turn on 256 color mode.
  set -g default-terminal "xterm-256color"

You can verify that true color support is working by starting a tmux session and issuing the tmux info command. Use exit to close tmux.

$ tmux
  $ tmux info | grep Tc
  197: Tc: (flag) true
  $ exit

You can see the Tc flag is available and set too true. If you don’t see that message you might need to upgrade your version of tmux. The number at the start of the output may be different for you, that isn’t an issue. As long as you see the (flag) true output the true color is active. True color support was not added until the 2.2 release. Make sure your tmux version is at least that high.

Status Bar Colors

The default tmux status bar is a bit overwhelming. That bright green is distracting. Now that tmux is set to use lots of colors we can fix that issue. First we will change the status bar to have a black background and white text. This will be a simple base to start from.

~/dotfiles/tmux/.tmux.conf
  # Black and white for the status area.
  set -g status-style fg=white,bold,bg=black

Again, we are using the -g flag to make this setting global for all tmux sessions. Using the status-style setting we make the foreground color white and bold, and the background color black. The color variables are made available by tmux. The named colors are black, red, green, yellow, blue, magenta, cyan and white. You can also use color0 through color255 to specify a specific color from the 256 color palette. You can see all of the colors and their numbers on this web page.

We used the bold modifier for the foreground color. Other modifiers are dim, reverse, blink and bright, which is a synonym for bold.

The tmux window list can be styled separately from the status bar. To make it easier to spot the active window we’ll change its color to red. The other windows will be colored cyan.

~/dotfiles/tmux/.tmux.conf
  # Window list colors
  setw -g window-status-style fg=cyan,bg=black
  setw -g window-status-current-style fg=red,bold,bg=black

That looks better. No more bright green and we get some information from the window list colors. One other change that is needed is the command line styles. If you activate the tmux command line with <Prefix>: it displays as a bright yellow. It is as bad or worse than the original status bar green. Add these lines to your tmux configuration to fix it.

~/dotfiles/tmux/.tmux.conf
  set -g message-style fg=white,bold,bg=black

That will make the command line styling consistent with the rest of the status bar. You can easily tell the difference between the two because the command line removes all of the other information on the status line.

Z Shell tmux Plugin

If you didn’t add the tmux Z Shell plugin to your Z Shell configuration file in the last chapter you should do it now. Enable the tmux plugin as shown below.

~/dotfiles/zsh/.zshrc
  plugins=(
    git
    tmux
  )

Those are all of the configuration changes we need for now. There will be some more later, but you probably want to get some hands on time with tmux already. Save the changes you made to the .tmux.conf and .zsh files and make sure to commit and push your changes to your remote host.

tmux Basics

Time to get some actual tmux usage under our belts. I’ll start with very basic tmux usage and build up to more complex tasks. Along the way I will show you how to create a full tmux session to control an entire project, then we will convert that knowledge into a tmux script that can be used to recreate that same session anytime you need to.

The simplest way to use tmux is simply to enter tmux at the command line. That will start a new unnamed tmux session.

$ tmux

Not that much will change. You will see the same terminal prompt and the screen will have cleared. You will also notice a status bar at the bottom of the terminal window. It shows that you are running zsh, your computer host name and the current time and date. You can do anything in the tmux session that you can do at your shell prompt. All of the completion and line editing features are still available.

tmux is organized into a hierarchy of objects. When you start tmux it creates a local tmux server. A tmux server can contain one or more tmux sessions. Each tmux session can contain one or more tmux windows. And each tmux window can contain one or more tmux panes. We won’t deal with tmux servers in this book. It is possible to attach to remote tmux servers, but that is a bit beyond the scope of this book. tmux sessions, windows and panes will be the primary focus.

Default tmux User Interface
Default tmux User Interface

To quit a tmux session just enter the exit command. Go ahead and exit this session.

$ exit
  no sessions
  [exited]

You will be returned to your original shell window. Any command output that was previously in the shell will still be there. You can also see a couple messages from tmux. It reports that there are no sessions remaining, and it has exited.

Named Sessions

You will most likely want to create named tmux sessions. This allows you to keep track of many tmux sessions and attach and detach from them as needed. If you don’t name a tmux session you can still have many sessions open and use them all, but they will be numbered, which will make it hard to remember the purpose of each session. To name a tmux session you use the new-session -s [name] tmux arguments.

$ tmux new-session -s project

You should notice that the status line now starts with the name of the session, in this case it is named project.

You can also shorten new-session to just new and it will work.

$ tmux new -s project

Attach And Detach

What do I mean by attaching and detaching from tmux sessions? This is one of the great features of tmux. You create a named tmux session, inside that session you can create additional tmux windows, split a single window into multiple panes, and so on. If you are done working in a particular tmux session, or just need to work in a different tmux session for a moment you detach from the current session, which drops you out to the main terminal shell, and then attach to the other tmux session, which can be a completely different environment.

In order to detach from a tmux session you use <Prefix>d. You can remember d as detach. If you remember from the last section on configuring tmux I had you change the prefix key sequence to <C-a>, so <Prefix>d really means press the <Ctrl> and <a> keys together then release both, then press the d key. The <Prefix> is a signal to tmux that the next keystrokes should be interpreted as a tmux command.

Lets try this out. Start the project tmux session again if you exited it earlier.

$ tmux new -s project

Change directory to the dotfiles project and list the files, just so there is some change to the window contents.

$ cd ~/dotfiles
  $ ll

Now we will detach from this tmux session. Press the <Prefix>d key sequence. You will be returned to the main shell window and see a message from tmux about being detached.

[detached (from session project)]

Create another tmux session named work.

$ tmux new -s work

You can see this is a fresh tmux session. The file listing from the project session is not visible here. Go ahead and detach from this session as well. Once you are back out to the main shell enter the following command.

$ tmux list-sessions

That will list all of the currently running tmux sessions. It shows the session name, how many windows are open in the session, when the session was created and the size of the terminal window. The list-sessions command can also be shortened to ls. If you only have one tmux session running you can attach to it simply by entering tmux attach. If you have more than one tmux session running you have to use the session name. To attach to the project session enter the following command.

$ tmux attach -t project

You will now be back in the tmux session named project. You will see the directory listing that was displayed earlier. Z Shell can complete parts of the tmux command. Type tmux at<Tab> and the rest of the attach sub-command will be completed. Then type -t pr<Tab> and the project name will be completed.

Ending A Tmux Session

As mentioned above, while you are inside a tmux session entering exit will close the current window. If that window is the only window open it will also end the tmux session. If you are not inside the tmux session you can use the kill-session command to kill a tmux session. This can come in handy if a program inside a tmux session is using too many resources or is locked up. As with the attach command you must supply the session name.

$ tmux kill-session -t work

Creating Windows

Now that you have the basics of starting, stopping, attaching and detaching from tmux under your belt we can start getting a bit more creative with the tmux session. A tmux window is similar to a tab in your terminal. It is another instance of the shell that you can run commands in.

When you start a tmux session it creates the first window for you automatically. That was shown on the status bar as 0:zsh*. What that is saying is, this is window number 0, and the current command is zsh, the asterisk lets you know that this is the currently selected window.

Having the current command as the window name is not terribly helpful for our purposes. You can name a window when you create it. Make sure any existing tmux sessions are closed and then enter the following command.

$ tmux new -s dotfiles -n editor

This will create a new tmux session and name the first window editor. The tmux status bar is starting to take on some more semantic meaning. To the far left is the session name, which should be named after the project being worked on. After that will be a numbered list of windows, each performing different functions within the project.

While inside a tmux session you can create new windows the the <Prefix>c key sequence. Think of c as create. That will add a new window to the list in the status bar and the name will default to the current command, zsh in our case. It also makes the new window the active window. You can see the new zsh window is marked with an asterisk.

You can rename a window using the <Prefix>, key sequence. That will change the status bar to an input field. It will show (rename window) zsh and the cursor will be at the end of the text. Simply backspace to clear out zsh and enter console as the name for the second window.

Moving Around

Now that you have two windows how do you move from one window to another? The create window command automatically places you in the new window. If you want to move to the previous window use <Prefix>p. Similarly, to move to the next window use <Prefix>n. The mnemonics, p for previous and n for next are simple enough to remember.

The previous and next commands will cycle through the list continuously. If you have three windows open, numbered zero thru two, and you are in the last window numbered two, pressing <Prefix>n will move you back to the first window numbered zero.

Moving a single window at a time is not always very efficient. If you had eight or more windows open constantly pressing <Prefix>n to get from the first window to the eighth window would be too slow. You probably noticed the number next to each window. Those numbers can be used to move to that window directly. So, <Prefix>0 would activate the window numbered zero. <Prefix>8 would activate the window numbered eight, and so on.

It can be a bit unintuitive to have the windows numbered from zero. Programmers are accustomed to zero based indexing, but from a key sequence perspective it is a bit strange. Having <Prefix>0, a key on the right side of the keyboard open a window at the far left of the window list doesn’t make a lot of sense.

We can change the default window and pane numbering settings in the tmux configuration file. Open it and add the following settings.

~/dotfiles/tmux/.tmux.conf
  # Set the base index to one.
  set -g base-index 1
  set -g pane-base-index 1

Now when you create a tmux session the first window will be numbered one, the second two and so on. The same will go for panes that are created within the window. The first pane will be numbered one, the second two, etc.

One other handy window movement shortcut is <Prefix>w. It will show you a list of windows in the current session. The currently selected window will be initially highlighted and be marked with an asterisk as usual. You can move through the list with the <Up> and <Down> keys. Pressing <Return> will activate the highlighted window. You can also press the number to the far left in parentheses to activate that window. Be careful here though because if you changed the base-index as discussed above the window number and list number will not match. The list numbers still start at zero.

Advanced tmux

So far I’ve covered creating tmux sessions, naming them, creating and naming tmux windows and moving around from window to window. Just knowing that much will get you pretty far with tmux, but there are some more very handy features that will take your tmux usage even farther. Here I will show you how to use the tmux command mode, how to create and manage tmux panes, tmux shortcuts for moving between sessions and finally some tmux aliases.

Command Mode

There is another way to create new windows. tmux has a command mode. If you enter the <Prefix>: key sequence you will see the status bar change color and the cursor will be placed there. This is the tmux command line. You can perform any action that tmux understands in command mode, from creating new windows to starting new sessions.

I find it to be particularly handy for creating new windows because you can create and name the window in a single step. To try it out press <Prefix>: and then enter the following command.

new-window -n server

That will add a new window to your tmux session and name it server.

You can also have a command run automatically when the window opens by passing in the command in quotes as follows.

new-window -n server "rails server -p 8080"

It is important to note, however, when you create a window and have it run a command immediately the window will automatically close when the command is finished. For a long running process like a development server this is probably ok. You likely do not need the window any longer when you kill the server. For a short one-off command this is not workable. The command will run, finish and the window will close before you can see the output. In those cases it is best to just create the window and then enter any commands you want yourself.

Panes

Having many windows available in tmux is certainly handy, but sometimes you want to see multiple shells at the same time. This is where tmux Panes come into play. You can split a single tmux window vertically, horizontally or both. There are also built in Pane Layouts that you can use to get just the right configuration for your needs. If none of the built in layouts work you can also resize your panes on your own.

Start a new tmux session named panes so you can see how they work.

$ tmux new -s panes

Once inside the tmux session pressing <Prefix>% will split the window from top to bottom and activate the new pane on the right. Now press <Prefix>" and the right side pane will be split in half from left to right. Again, the new pane on the bottom will be the active pane.

You now have three panes, three shells, open in this tmux window. How you do move around from pane to pane? You can cycle through the panes using <Prefix>o or you can move in any direction you want by using <Prefix><Up>, <Prefix><Down>, <Prefix><Left> or <Prefix><Right>.

Better Pane Keys

The percent and double quote characters are not terribly memorable for creating panes. Lets remap them to the vertical bar (|) and the dash (-) characters. Open your tmux configuration file again and add the following lines at the end. Those keys should be easier to remember. If you want one pane on the left and one on the right use <Prefix>|. If you want one pane on top and one on bottom use <Prefix>-.

~/dotfiles/tmux/.tmux.conf
  # Create panes with | and -.
  bind | split-window -h
  bind - split-window -v

Moving Panes

If you don’t like the position of the current pane you can move it. The <Prefix>{ (open brace) will move a pane to the previous position. Using <Prefix>} (close brace) will move the pane to the next position. The previous and next positions depend on the layout you are using. For example, if you are using even-vertical the previous position will move the pane up in the window, and the next position will move the pane down in the window.

Resizing Panes

Once you have some panes open in a window you can cycle through a set of built in pane layouts. Use the <Prefix><Space> command. There are five layouts, even-horizontal, even-vertical, main-horizontal, main-vertical and tiled. The even-horizontal will make all of the open panes evenly spaced top to bottom panes. The even-vertical layout does the same but left to right. The main-horizontal and mail-vertical keeps the first pane in its current size and location, and the remaining pains work like even-horizontal or even-vertical. The tiled layout will attempt to space out the panes evenly in a square pattern. Try opening a number of panes and then cycle through the layouts. You will see the patterns easily.

If you want finer control over the size of your panes you can resize them yourself. With Mouse Mode enabled you can simply click on one of the pane lines and drag it to the desired size.

There is also a resize-pane command. It works on the active pane and can be used to resize a pane in any direction. Remember you can enter command mode with <Prefix>:. Suppose you have a left and a right pane. If the left pane is active and you wanted to increase its size you would enter command mode and run resize-pane -R {N}. The -R argument means to increase the size of the current pane by moving the border right. The optional {N} argument is the number of columns to increase the size. If you do not supply it it will default to one. You could decrease the size of the current pane by using the -L argument, which means to move the pane border left. The same rules apply to -U and -D, which move the pane border up or down respectively.

It will get old having to type that command over and over to get your panes to the size you want. This is a good opportunity to create some key bindings. Open your tmux configuration file and add the following commands.

~/dotfiles/tmux/.tmux.conf
  bind -r H resize-pane -L 5
  bind -r J resize-pane -D 5
  bind -r K resize-pane -U 5
  bind -r L resize-pane -R 5

Save the file and restart any open tmux sessions. You can now resize your pane by using <Prefix>H, <Prefix>J, <Prefix>K, or <Prefix>L. Note that the bindings are for the uppercase letters, so you will need to use the <Shift> key to activate them. The -r argument to bind means that the key binding is repeatable. That means you can press the <Prefix> sequence once, then L repeatedly to keep increasing the size. Those letters were chosen to reflect the Vim movement directions. The H and L keys will move a vertical pane border left and right respectively. The J and K keys will move a horizontal pane border down and up respectively.

Session Shortcuts

You already know how to detach from the current session with <Prefix>d and attach to a different session with tmux attach -t [name]. That may seem like a lot of work to just move to a different session though? You can actually move between sessions without detaching first. Use <Prefix>( (open paren) to move to the previous session and <Prefix>) (close paren) to move to the next session. Keep going in the same direction and you will wrap around to the beginning of the list, just like cycling through windows.

You can move to the last previously used session with <Prefix>L, and see a list of available sessions with <Prefix>s. When viewing the session list the current session is initially highlighted, and marked with an (attached) annotation. Use the <Up> and <Down> keys to move through the list. Pressing <Return> will activate the selected session.

Z Shell Aliases

When you added the tmux plugin to your Z Shell configuration it enabled a number of tmux aliases. These will shorten your tmux commands considerably.

When creating a new tmux session, instead of typing out tmux new-session -s project, you can instead use ts project. If you also wanted to name the window you could use:

$ ts project -n editor

Getting a list of the active sessions with tmux list-sessions is also shortened to tl.

Attaching to an existing session, normally done with tmux attach -t [name], is shortened to ta [name].

If you need to kill a tmux session, normally performed with tmux kill-session -t [name], you do it with tkss [name].

Scripted Environments

This is one of the greatest benefits of tmux. Here you will learn how to create a short tmux script that will create a tmux session with as many windows as you need, and start commands in specific windows. Once you have this script setup all you have to do is run the script to get your entire development environment up and running whenever you need it.

There are really only two concepts you need to understand about tmux scripting. First, tmux is really built on commands. You have seen a number of commands already, new-session, new-window and attach are all tmux commands. Many commands are bound to shortcut keys, but they can also be used on their own as shown in the Command Mode section.

Second, you can send commands to a tmux session that you are not currently attached to using the -t (target) argument. This is what you are doing when you attach to a named session. You issue the attach command to the -t target session. You can also send the new-window command to a target session, or any other command.

Dotfiles Development

We’re going to set up a mini development environment for our dotfiles project. This will give us a good handle on the basics and allow you to create other tmux scripts for your other projects. To start, create a file named dotfiles-dev in the ~/dotfiles/scripts directory. We’ll also turn on its executable flag so it will be runnable.

$ touch ~/dotfiles/scripts/dotfiles-dev
  $ chmod +x ~/dotfiles/scripts/dotfiles-dev

Edit the file and add the following line to create the initial tmux session.

~/dotfiles/scripts/dotfiles-dev
  tmux new -s dotfiles -n editor -d

This should all feel like familiar ground so far. This command creates a new tmux session named dotfiles and names the first window editor. The new argument, -d, is telling tmux to automatically detach from the tmux session. We want to operate outside of the session while we are setting up the environment.

Now that the session is created we want to make sure that it is located in the correct directory. You can have tmux run commands in a targeted session using the send-keys command. Lets have our script change directory into the dotfiles project.

~/dotfiles/scripts/dotfiles-dev
  tmux send-keys -t dotfiles 'cd ~/dotfiles' C-m

The C-m at the end of the line tells tmux to press the <Return> key. Essentially that line of script is typing cd ~/dotfiles<Return> into to dotfiles tmux session and executing the command.

This window is named editor, so why not start up an editor automatically. For now it will be nano, but you could also replace that with $EDITOR to start the system editor.

~/dotfiles/scripts/dotfiles-dev
  tmux send-keys -t dotfiles 'nano' C-m

You should be able to see the pattern here. You can run any command you like in a tmux session with the send-keys command. Just specify the target session, surround the shell command you wish to execute in quotes and use C-m to run the command.

That is all we need to do for the first window, which is our dedicated editor window. Now on to the second window which will be the console window for entering regular terminal commands.

~/dotfiles/scripts/dotfiles-dev
  tmux new-window -t dotfiles -n console

The console should also be moved to the correct directory, and since creating a new window makes it the active window we can just use the send-keys command again.

~/dotfiles/scripts/dotfiles-dev
  tmux send-keys -t dotfiles 'cd ~/dotfiles' C-m

That is all we need for this particular development environment. You could use these same commands to create additional windows, change directory to the project directory and start servers or other tools. You can expand on this pattern as much as you need to for your own projects.

Once everything is setup the way you want you can select the window you want to be active with the select-window command. In this script we’ll make the editor window active so we can dive right into getting some work done.

~/dotfiles/scripts/doftiles-dev
  tmux select-window -t dotfiles:1

You will notice that the target also includes :1. This is how you refer to windows in tmux. The format is [session]:[window].[pane]. The session name comes first, if the session isn’t named you would use the session number. Following the session is a colon and the window number. Window number 1 is used here because the tmux base-index setting was changed in the Moving Around section so that window numbers would start at one instead of zero. If you had split the window into two panes and wished to make the second pane active you would reference it as dotfiles:1.2.

Now that you have selected the window, and possibly the pane you want to be active, you can attach to the session. Add this as the last line in the tmux script.

~/dotfiles/scripts/dotfiles-dev
  tmux attach -t dotfiles

Save the script and test it out by entering dotfiles-dev at the command line. You should end up inside a new tmux session named dotfiles there should be two windows, one named editor and the other named console and the nano text editor should be open in the editor window. From here you are ready to edit any file you need to in nano and perform any command line operations just by pressing <Prefix>n to move to the console window.

This is a very basic development environment, but you should be able to see how it can easily be extended to add additional windows. Just add a couple lines to the script to create a new window, change directory into the project directory, and execute any command you like.

Team Scripts

The approach above works well for small projects that only one or two people work on, but it isn’t very workable for larger teams. You wouldn’t want to hard code the project path, for one thing, because other people on the team might use different directory structures. You would probably want to keep the tmux setup script in the project’s git repository as well to keep it up to date for everyone.

I’ve been calling these scripts tmux scripts, because that is the only command being used, but these are actually shell scripts and they can use shell scripting features such as sourcing other scripts and using variables. We can combine those features to create tmux scripts that will work for anyone on the development team.

I will modify the existing dotfiles script above to this new style. It is probably overkill for this project, but it will be helpful to see the changes in relation to what we’ve already built. To start, copy the existing ~/dotfiles/scripts/dotfiles-dev file to ~/dotfiles/dotfiles_tmux.sh.

$ cp ~/dotfiles/scripts/dotfiles-dev ~/dotfiles/dotfiles_tmux.sh

The ~/dotfiles/dotfiles_tmux.sh script will be where all of the actual work gets done. It will be modified to use a variable that will be set in the ~/dotfiles/scripts/dotfiles-dev script. Open the ~/dotfiles/scripts/dotfiles-dev file in nano and remove the existing script content. Completely empty out the file, then add the following lines.

~/dotfiles/scripts/dotfiles-dev
  # Set project_dir to the root directory for the given project.
  project_dir=$HOME/dotfiles
  
  # Run the actual tmux startup script for this project.
  source "$project_dir/dotfiles_tmux.sh"

The first line sets up a variable specific to this machine for the location of the project. This allows for the actual project to be in any directory. Each developer would create their own dotfiles-dev script, somewhere in the PATH, and set the project_dir variable to the their local projects directory. The second line then sources the actual tmux environment setup script, using the project_dir variable to locate the script.

This dotfiles example is a bit contrived because the dotfiles project will always be be at the same location; in your home directory. But you should be able to see how this adds flexibility for multiple developers.

Now we will modify the ~/dotfiles/dotfiles_tmux.sh file to make use of the project_dir variable. Save the dotfiles-dev file and then open ~/dotfiles/dotfiles_tmux.sh. You will have to change the send-keys commands that change the directory to double quotes to be able to use the project_dir variable. The final file is shown below.

~/dotfiles/dotfiles_tmux.sh
  # Create the 'dotfiles' tmux session.
  tmux new-session -s dotfiles -n editor -d
  
  # Change directory to $project_dir.
  tmux send-keys -t dotfiles "cd $project_dir" C-m
  
  # Open Nano.
  tmux send-keys -t dotfiles 'nano' C-m
  
  # Create the console window and set the directory.
  tmux new-window -t dotfiles -n console
  tmux send-keys -t dotfiles "cd $project_dir" C-m
  
  # Select the editor window.
  tmux select-window -t dotfiles:1
  
  # Attach to the session.
  tmux attach -t dotfiles

In most situations the project_name_tmux.sh file will actually be stored in the project’s Git repository. It can be kept up to date with the project. You can add new windows to run servers as needed and everyone will get the correct development environment when they update their project.

Clean Up

There are a couple things that can be done to improve this tmux script. First, as I mentioned above this is a shell script, so we could use the change directory command to set the directory before tmux is started. That would remove the need to change the directory for each tmux window. We can also verify that the directory exists and report an error if it doesn’t. Add the following lines to the top of the dotfiles_tmux.sh script.

~/dotfiles/dotfiles_tmux.sh
  # Verify that the project_dir variable is set to an actual directory.
  if [ ! -d $project_dir ]; then
    printf "%b" "The project_dir variable is not set to a valid directory." >&2
    exit(1)
  fi
  cd $project_dir

The first line checks for the existence of the directory. The square brackets are shorthand for the test command. It could also have been written as if test ! -d $project_dir; then. The ! is the not operator, the -d option makes the test command check for a directory. The if statement boils down to ‘if $project_dir is not a directory’. If that evaluates to true the error message is printed and the script is exited with a non-zero result. Otherwise $project_dir is a directory, and we just change directory to it right below the if statement. You can also remove the existing send-keys commands that change the directory.

Second, we can test if there is already a tmux session with the same name. If so we don’t need to setup anything, we can just attach to the existing session. We can use the tmux has-session command to check for an existing session. Again, at the top of the dotfiles_tmux.sh file add the following code.

~/dotfiles/dotfiles_tmux.sh
  # Check for an existing dotfiles session.
  tmux has-session -t dotfiles
  if [ $? != 0 ]; then
    # The dotfiles session does not exist, so set it up here.
    # The tmux attach line is moved outside.
  fi
  tmux attach -t dotfiles

First we use tmux to check for a session named dotfiles. Shell commands return a zero to signify success, so if the dotfiles session does exist the return value will be zero, otherwise the dotfiles session does not exist. The $? shell variable is set automatically and holds the return value of the last run command. The if statement here is basically, ‘if the last command failed’. If that condition is true we run the majority of the existing tmux setup script. Notice that the tmux attach command was move outside of the if block. In both cases we want to attach to the tmux session, so that command will always be run.

The final script is shown below.

~/dotfiles/dotfiles_tmux.sh
  # Setup or attach to an existing dotfiles tmux development environment.
  # A 'project_dir' variable should be initialized prior to running this script
  # and should point to the local root directory for the project.
  
  # Check for an existing dotfiles session.
  tmux has-session -t dotfiles
  if [ $? != 0 ]; then
    # Verify that the project_dir variable is set to an actual directory.
    if [ ! -d $project_dir ]; then
      printf "%b" "The project_dir variable is not set to a valid directory." >&2
      exit 1
    fi
    cd $project_dir
  
    # Create the tmux session.
    tmux new-session -s dotfiles -n editor -d
  
    # Open Nano.
    tmux send-keys -t dotfiles 'nano' C-m
  
    # Create the console window.
    tmux new-window -t dotfiles -n console
  
    # Select the editor window.
    tmux select-window -t dotfiles:1
  fi
  tmux attach -t dotfiles

Summary

This has been a quick, but pretty thorough, chapter on tmux. You learned how to start tmux, how to create new sessions, how to attach and detach from sessions, how to create new windows and panes within a session, and how to move around between those windows and panes in a session. Finally you learned how to create scripts that can setup an entire tmux development environment for your own or your team’s projects.

Now that we have a strong foundation for terminal based development with Z Shell and tmux we will dive into actually editing files with Vim. Prepare yourself for a complete overhaul in how you approach writing and editing code.