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 -v | Output detail |
---|---|
Errors only | |
-v | Above plus Warnings |
-vv | Above plus Informational |
-vvv | Above plus Debug |
-vvvv | Above 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:
Subcommand | Getopt | Intended action |
---|---|---|
ls | -l | List running sessions |
s | -s | Open SSH session to the destination |
ms | -ms | Open multi SSH sessions to hosts, synchronizing input |
k | -k | Kill a session |
b | -b | Break a multi-session pane into single windows |
j | -j | Join multiple windows into one single one with many panes |
help | -h | Print this message or the help of the given subcommand(s) |
-v[vvv] | Verbosity, the more v, the more log output | |
-n | Open second session to the target, do not attach to existing | |
-g | Group 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 adate
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