Introduction

tm is a small helper/wrapper for tmux. It aims at making the creation and resume of sessions easier wile trying to stay out of the users way.

History

tm started as a bash shell script to ease my day-to-day work with tmux. While tmux itself is easy to work with (creating new sessions is simple) for simple tasks, it takes considerably more brain power to setup a session that opens connections to multiple hosts and sets tmux up in the way, that input is send to all of them at the same time.

And this being a task I need a lot of times every day, I do not want to do this manually, so tm was born. Back in 2010/2011 when I started working on it, there wasn't any (to me) useful tool I could have taken instead.

Rust

At some point in time I switched over from Shell to Rust, which brought a huge speed boost for tm. While the shell version always worked good enough and for most sessions one builds also fast enough, having a compiled binary still makes a difference. It is especially noticable on the sessions that open dozens (or even hundreds) of windows/panes.

Commandline

tm offers simple ways to start new sessions using commandline, where it supports two styles of options - so-called traditional and getopt style. They are mostly identica, though some options only work in one and not the other way.

Config files

tm also offers different types of config files (called session files). From simple list of hostnames to files that ca include others to files that basically contain tmux commands to execute.

Commandline

As tm started out as a small shell script, not much thought was given to the way of calling it, it just accumulated options. That is what is now labeled traditional style. Some of those, like the s and ms sub-commands are still the most used arguments to tm - typing their getopt equivalents of -s and -m is just more/less nice to type.

Common beheaviour

No matter if one uses traditional or getopt style, common options like -v/-q/-h as well as their long versions --verbose/--quiet/--help behave the same.

Log output

tm includes extensive logging. To see it, use the -v/--verbose switch. Use it more often for more detailed output - with TRACE level being the most detailed one, including tracing of function calls and timing information for them.

Default is to only output error messages.

Count of -vOutput detail
Errors only
-vAbove plus Warnings
-vvAbove plus Informational
-vvvAbove plus Debug
-vvvvAbove plus Trace

Usually a -vvv is really helpful to debug new session files.

Traditional (subcommand) vs getopt calling style

The traditional calling style (subcmmand style), some of it dating back to the first shell script version, is easiest for some options. Say, a tm s host is less to type than tm -s host, same for ms|-ms, though that may be personal preference too. A few options are only available using getopt style, for example the -g or -n.

Overview

As of tm version 0.9 the following commands/options are supported, explained in more detail below:

SubcommandGetoptIntended action
ls-lList running sessions
s-sOpen SSH session to the destination
ms-msOpen multi SSH sessions to hosts, synchronizing input
k-kKill a session
b-bBreak a multi-session pane into single windows
j-jJoin multiple windows into one single one with many panes
help-hPrint this message or the help of the given subcommand(s)
-v[vvv]Verbosity, the more v, the more log output
-nOpen second session to the target, do not attach to existing
-gGroup session - attach to an existing session, but keep seperate window config

ls / -l

Same as tmux ls, simply list the existing tmux sessions.

s / -s [HOSTS]

This will open a new tmux session and create multiple windows, one per argument - the windows will be using ssh to connect to the given host(s). It's arguments can be anything that your SSH config will accept, this can range from simple hostnames to user@host or whatever possible matches you may have configured in your ~/.ssh/config file.

Multiple arguments will open multiple tmux windows in the same session.

ms / -ms [HOSTS]

This will open a new tmux session and create multiple panes in the first window, one per argument. The panes will be using ssh to connect to the given hosts. It's arguments can be anything that your SSH config will accept, this can range from simple hostnames to user@host or whatever possible matches you may have configured in your ~/.ssh/config file.

When all panes are opened the tmux switch synchronize-pane will be toggled on, so that anything input will be sent to all hosts at the same time.

This subcommand will check if tmux has been able to open the pane, and if not it will tell tmux to rearrange the layout, to "gain" some space again. At the end it will select the tiled layout, so that all panes will roughly have the same size.

k /-k [SESSION]

This basically translates to tmux kill-session and will kill the session with the given name.

b /-b [SESSION]

This is used to break a session built using ms or a session file (or even manually) into seperate windows. It will take the given session name and split all panes in the window included into seperate tmux windows.

j / -j [SESSION]

This is used to join a multi-window session to one that closely resembles one build using the ms command. It will take the given session name and join all windows to the first window, as such making them panes of the first window. Afterwards it will toggle the synchronize-pane option of that window on, so any input will be send to all panes.

-n

This is only valid as an addition to s/-s / ms/-ms and will ensure, that a new session will be opened, even if the arguments passed would otherwise reopen an already existing session. That way one can have multiple, independent, sessions open to the same set of arguments.

Example:

tm s host1 host2
tm -n s host1 host2

This will open two sessions to host1 and host2, in two different tmux sessions, with the second session having a random string added to the session name. A tm ls will look similar to the following:

s_29814_host1_host2: 1 windows (created ...)
s_host1_host2: 1 windows (created ...)

-g

This is only valid as an addition to s/-s / ms/-ms and will attach to an existing session - but with a seperate window config - tmux calls this a session group. Citation from tmux manpage about it:

[...] a session group. Sessions in the same group share the same
set of windows - new windows are linked to all sessions in the group
and any windows closed removed from all sessions. The current and
previous window and any session options remain independent and any
session in a group may be killed without affecting the others. The
group-name argument may be:

So in effect one has the same session with the same windows and panes open, but one can display and use different windows at the same time.

Session files

Anyone who carefulle read the section about the commandline and compared it with the actual output of tm -h/tm --help may have noticed that one thing hasn't been mentioned there - -c <CONFIG>.

That simple option turns out to be big enough to warrant an own chapter - actually seperated into two - and so here goes.

Session files exist in (currently) two different versions, currently named Simple and Extended, and their naming already sets expectations.

File locations

Session files are expected at ${HOME}/.tmux.d and are plain text files. Files with no extension are handled as simple, files with a .cfg as extension as extended session files.

Replacement tokens / tilde and variable expansion

Session files support the expansion of (Shell) variables and one replacement token. They also support tilde extension.

Tilde extension

The LIST command used in session files supports tilde expansion, so ~/ as well as ~username/ is supported syntax.

Variable expansion

Shell variables are expanded in LIST commands and extended session files. Both, $HOME as well as ${HOME} (with/without {}) are supported syntax. Expanding an unset variable will return an empty string.

Replacement token

In addition to shell variables, for historical reasons, tm supports one replacement token for session files. Within session files it is written as ++TMREPLACETM++ and will get replaced with the argument following the session filename on commandline. That is, in tm vm_nfs mail the word mail will be the replacement. Unlike Variable expansion, this also works for the session name.

Simple

Simple session files are nothing more than files without an extension to their name, where each line is treated according to the following spec:

  • Line 1: Session name
  • Line 2: Ignored, but required for backward compatibility
  • Line 3 ...: Argument line, one of two possibilities:
    • Argument for SSH
    • The word LIST followed by a shell command.

LIST command

If an argument line starts with LIST, everything following it is used as a shell command. The commands stdout is read and every line read is treated as another argument line. This is recursive, so a line read by LIST can include another LIST (until stack overflows, so be careful to not create an endless loop).

Full Shell expansion, that is both tilde and environment variables, is supported. Details can be taken from the shellexpand rust crates documentation, but in short: ~ expands to the users homedir, $VAR and ${VAR} expand to the content of the variable VAR, and non-existing variables expand to an empty string.

Example

Line numbers (if any) added for clarity in the description later, remove them if you copy this example!

Cluster of machines

somecluster
NONE
host1
user@host2

This will open a session named "somecluster", connecting to host1 using the default user (usually your username, or whatever your ssh config says, if any) and user@host2. It will open one window with two panes and synchronize input to them, so that any action happens on both hosts.

Machine list from external command

nfsserver_mail
NONE
LIST ssh -tt clustermaster "sudo /usr/sbin/gnt-instance list --no-headers -o name --filter '(\"nfs\" in tags and \"prod\" in tags and \"mail\" in tags) and admin_state == \"up\"'"

This needs sudo for the gnt-instance command on the cluster master, recommended NOPASSWD, and will then fetch a list of (running) hosts where the ganeti tags match nfs, prod and mail. Any other host, or hosts with those tags but marked down, will be ignored.

Extended, machine list from external command using ++TMREPLACETM++

nfsserver_++TMREPLACETM++
NONE
LIST ssh -tt clustermaster "sudo /usr/sbin/gnt-instance list --no-headers -o name --filter '(\"nfs\" in tags and \"prod\" in tags and \"++TMREPLACETM++\" in tags) and admin_state == \"up\"'"

This is nearly the same as the example just before, except that the mail function is replaced by a token. This token can be given as one more argument after the session file and allows flexibility in the LIST command. To open the same session as in the example before, that is, all mail VMs, one can run tm vm_nfs mail - assuming the session file is saved as vm_nfs in the .tmux.d.

Extended

Extended session files are files with the ending .cfg, where each line is treated according to the following spec:

  • Line 1: Session name
  • Line 2: Ignored, but required for backward compatibility
  • Line 3 ...: Argument line

Argument line

An argument line can contain any valid tmux command, same as one can enter them in shell directly (but without the leading tmux). Additionally the following strings will be replaced:

  • SESSION - will be replaced by the session name
  • TMWIN - will be replaced by the current window number. This number is internally increased every time an argument-line contains the string "new-window".

Full Shell expansion, that is both tilde and environment variables, is supported on argument lines. Details can be taken from the shellexpand rust crates documentation, but in short: ~ expands to the users homedir, $VAR and ${VAR} expand to the content of the variable VAR, and non-existing variables expand to an empty string.

Example

Line numbers added for clarity in the description later, remove them if you copy this example!

1 simpleexample
2 NONE
3 new-session -d -s SESSION -n SESSION ssh -t localhost 'TERM=xterm sleep 90'
4 split-window -h -p 50 -d -t SESSION:TMWIN ssh -t localhost  'watch -n1 -d date -u'
5 split-window -v -p 70 -d -t SESSION:TMWIN ssh -t localhost  'TERM=xterm htop'
6 set-window-option -t SESSION:TMWIN automatic-rename off
7 set-window-option -t SESSION:TMWIN allow-rename off

This example will open a tmux session named simpleexample.

  • Line 1 is the session name
  • Line 2 is ignored
  • Line 3 opens a new session, with the name taken from Line 1. It will start ssh to localhost with a sleep 90 running in it.
  • Line 4 splits the window, horizontally, with a given size, running a watch on a date command.
  • Line 5 splits that window again, vertically, with a given size, opening a htop.
  • Line 6 disallows tmux to rename the session windows
  • Line 7 sets another option disabling renames

Generated 2023-08-14 04:39:44 +0000
v0.9.0 2023-08-14 04:39:10 +0000