More elegant way to unlink two tmux clients sharing a session from inside tmux

configurationdot-filestmux

This is almost a code-review question, but very *nixy and more about configuration than code. For years since I switched to tmux from screen I've had a couple of nagging issues that I could not make work the way I wanted them to. Since I setup my shell to exec into a tmux session I finally decided it was time to fix this one.

By default when you attach two tmux clients to the same session, they are locked together. Switch panes in one and both switch in sync. This is different from screen where the default behavior was a shared set of panes but the client itself was independent.

What I've wanted was a way to un-bind two tmux clients attached to the same session. This is easy enough to do if you are outside of tmux and launching a new client. Just start a new session with a reference to the parent one:

tmux new-session -t original_session -s new_session

However try do do this after you are in a client and want to unlink it from whatever other client is sharing that session and things get more complicated. I tried dozens of ways and never hit on one that worked well. The closest I got was using bind <key> prompt-command ... to prompt for both the old and new session names. For whatever reason tmux does not expand the #{session_name} variable inside arguments for a new-session or prompt-command. It does in arguments to many other commands (e.g. I use it in run below) but without that expansion this particular task becomes suddenly complicated. Even with manually entering the values, it only ever sorta worked.

Today I finally got it to work. The result, however, is uglier that I would have liked. It requires a shell function to do most of the magic. In order to contain this to just the config file without involving external scripts (for dotfile portability) I'm using a hack. The config, if you crop off the first two columns, is actually executable as a shell script. With everything wrapped in functions, you can call just the part of the script you need from any keybinding. The shell script bits are comments in the config, while in shell script made the config bit is discarded with a here-doc.

Todays solution came out something like this (my full .tmux.conf file):

# : << ENDOFCONF

# Clear the default binding
unbind C-c
# Pass the name of the current session to a function to clone it
bind C-c run "cut -c3- ~/.tmux.conf | sh -s clone #S"

# ENDOFCONF
# clone () {
#   orig=${1%-*}
#   copy="$orig-$(($( tmux list-sessions -F '#S' | sed -nE "/^${orig}-[0-9]+$/{s/[^0-9]//g;p}" | tail -n1 )+1))"
#   TMUX= tmux new-session -d -t $orig -s $copy
#   tmux switch-client -t $copy
#   tmux set -q -t $copy destroy-unattached on
# }
# $@

Mind you this works. From a running tmux client, Ctrl+B Ctrl+C creates a new session with a number appended to the name of the current one, uses the current one as a target for the panes, and switches to it.

I'm happy with this except for the nagging idea that I've just made something simple much more complex than it needs to be. Is there a simpler way to achieve this?

Best Answer

This is what I did:

alias ta='export NEW_SESSION=\$(uuidgen) ; tmux new-session -t 0 -s \$NEW_SESSION ; tmux kill-session -t \$NEW_SESSION'

What this does is create a new session from session named '0' (what the first session is named if you don't specify), named something uniquely random (using uuidgen) and then kill it when I detach.

Note that I'm using zsh. 'ta' is short for 'tmux attach'

Related Question