Saving time with a preset tmux setup

Programming is all about doing the same thing... over and over again, ad nauseam. No, not the computer, the poor programmer as they work on similar projects bouncing around all the different tools.

For example, let's take Drupal development - usually once you are in the midst of it, you may be accessing:

  • An editor (Vim, naturally)
  • A terminal for running system commands e.g. drush cr, drush cedit, drupal generate:module etc.
  • Maybe a todo list / notes
  • Log output
  • Watch output e.g. npm run watch
  • Test output e.g. phpunit, codeception etc.
  • File ops e.g. ranger
  • Reference material
  • etc.

That may end up looking a bit like this:

Drupal development screenshot

Note the Tmux status, bottom left, where it shows that I am in the first window of three.

As this is typical, wouldn't it be nice if, I don't know, you could run a command such as tmd <name of session> and it would set everything up just so. Also, over time, we could then refine this setup and get all the benefits of finessing it - or have multiple scripts depending on the type of project.

The script

No problem, take a look at this script drupal.sh:

#!/bin/zsh

# Note that this assumes base index of 1

# check for existence of required things
# $1 is the name of the window
# we are in the directory of the drupal project

if [ $# -eq 0 ]
  then
    echo "No arguments supplied, requires name of window."
    exit 1
fi

CWD=$(pwd)
SESSION_NAME="$1"

# detach from a tmux session if in one
tmux detach > /dev/null

# Create a new session, -d means detached itself
set -- $(stty size) # $1 = rows $2 = columns
tmux new-session -d -s $SESSION_NAME -x "$2" -y "$(($1 - 1))"

tmux new-window -t $SESSION_NAME:1 -n 'code'
tmux new-window -t $SESSION_NAME:2 -n 'logs'
tmux new-window -t $SESSION_NAME:3 -n 'zsh'

## Logs window
tmux select-window -t $SESSION_NAME:2

# Start up the logs listener
tmux send-keys "vbin/tail -f /var/log/apache2/error.log | clog drupal" C-m

## Zsh window
tmux select-window -t $SESSION_NAME:3
tmux rename-window 'Zsh'

## Main Window
tmux select-window -t $SESSION_NAME:1
tmux rename-window 'code'

# Split into left and right
tmux split-window -h -p30

# Right ready for taking commands / tests.
tmux select-pane -t 2
tmux send-keys "figlet -f roman Ready! | lolcat -t" C-m

# Left for neovim.
tmux select-pane -t 1
tmux send-keys "v" C-m

# Finally attach to it
tmux attach -t $SESSION_NAME

How it works

The process can be broken down as follows:

  • Creating a new session
  • Creating windows
  • Creating panes
  • Sending commands to the panes / windows

Creating a new session

Creating a new session is as simple as tmux new-session -s <name-of-session>. In the example script I have a bit more going on:

# Create a new session, -d means detached itself
set -- $(stty size) # $1 = rows $2 = columns
tmux new-session -d -s $SESSION_NAME -x "$2" -y "$(($1 - 1))"

What I am doing here is getting the current screen resolution and then creating a detached session of that size - the reason being that if you don't specify a size using the -x parameter then if you try to specify window split sizes it will wig out since it has no point of reference for "size".

Creating windows

Creating a window can be accomplished via tmux new-window -n <name-of-window>. In the example we have:

tmux new-window -t $SESSION_NAME:1 -n 'code'
tmux new-window -t $SESSION_NAME:2 -n 'logs'
tmux new-window -t $SESSION_NAME:3 -n 'zsh'

Here we create three windows - the -t flag is so that we can specify the target of the windows, which in this case is the session that we just created and the index of where they should appear.

We can then target windows with tmux select-window -t <name-of-session>:<index> for further commands.

Creating panes

Often you will want to split up a window into multiple panes. This can be done with something like tmux split-window -h -p<percentage> where -h signifies a horizontal split (guess how a vertical split is specified?) and -p signifies a percentage split. In the example I create a 30 percent split but you can of course specify by pixel if that is your jam.

Once you have created one or more splits, you can target panes with tmux select-pane -t <index-of-pane> for more commands.

Sending commands to the panes / windows

Lastly, once we have our windows and panes ready, we can send them further commands to start things up. Some examples from the above:

  • tmux send-keys "vbin/tail -f /var/log/apache2/error.log | clog drupal" C-m

(run the tail command in watch mode against the apache error log, piped into clog - the C-m is used to send an <Enter> to a pane or window)

  • tmux send-keys "figlet -f roman Ready! | lolcat -t" C-m

(run figlet with the string "Ready!", piped into lolcat in true colour mode )

Finishing up

Rounding out the script we have a call to tmux attach -t <name-of-session> which makes it visible. In the script, the name of the session is passed in as a parameter. For extra convenience I have the following alias in my .zshrc to run this script:

alias tmd='~/.config/tmux/drupal.sh'

Obviously you don't have to attach to a Tmux session - if you had the need, your script could start up all kinds of processes and then you could attach only to check it whilst doing other things (or not!). Tmux also supports multiple people attaching to a session, sending commands to specific sessions, saving sessions (via plugins) etc. The possibilities runneth over.

// @Series
// @Category
// @Size
// @
16/09/2018