Based upon my days and days of researching this, I may be seeking the impossible.
Situation
I have my
.bashrc
nicely configured to either spawn a newtmux
on connect, or if one exists, attach to it.I always hit Ctrl+D to exit a shell session. It's burned into my muscle memory. Unlearning it after thirty years of systems administration is also asking the impossible.
I want to be able to detach from
tmux
using just Ctrl+D, rather than having it kill my shell.
My imperfect approach
I can bind Ctrl+D to detach
in .tmux.conf
.
The problem is that I also have emacs
key-bindings burned into my muscle memory, so when I start editing a command line, I'll hit Ctrl+D to employ the GNU readline 'delete char under cursor'. Instead, the tmux
binding swallows the Ctrl+D, so I'm immediately detached. Same thing if I'm editing in emacs
.
Another flawed approach
GNU readline will take the EOF on an empty line, and then exit the shell. So I've tried trapping exit in .bashrc
instead:
trap "~/tmuxexit" EXIT
with the contents of tmuxexit
being:
tmux detach-client -s main
Which initially seemed like it worked, as hitting Ctrl+D on an empty line would report:
[detached (from session main)]
But tmux ls
reports no server running on […]
.
Do I seek the impossible?
Answer
I'm not sure if I crafted the simplest possible solution. This:
stty eof '^T'
bind '"\C-d": "\C-x\C-t\C-x\C-d"'
bind -x '"\C-x\C-t": _tmux_detach'
_tmux_detach() { [ -z "$READLINE_LINE" ] && tmux detach-client; }
bind '"\C-x\C-d": delete-char'
Explanation:
stty eof '^T'
makes your terminal no longer send EOF at Ctrl+D. Now it's Ctrl+T. The new key combination will behave like the old one in readline, I mean it will exit the shell only if the line is empty. I deliberately chose Ctrl+T because it's very unlikely you will want to use its default binding (transpose characters) with an empty line.I thought that disabling EOF entirely (
stty eof undef
) would work, but it turned out readline(?) still reacts to the previously defined combination (like Ctrl+D) and makes the shell register EOF if the line is empty.bind '"\C-d": "\C-x\C-t\C-x\C-d"'
makes Ctrl+D "send" Ctrl+X, Ctrl+T and Ctrl+X, Ctrl+D. We will utilize the two sub-sequences separately.bind -x '"\C-x\C-t": _tmux_detach'
– From now on Ctrl+X, Ctrl+T executes_tmux_detach
…… which is a function that detaches
tmux
if the line ($READLINE_LINE
) is empty.bind '"\C-x\C-d": delete-char'
– From now on Ctrl+X, Ctrl+D deletes the character at point, like Ctrl+D usually does.
So Ctrl+D will work like this:
- if the line is not empty, the function is a no-op and
delete-char
does its job; - if the line is empty, the function does its job and
delete-char
is a no-op (it gets triggered but has nothing to do).
Notes:
READLINE_LINE
was introduced in Bash 4.0.- If you paste the code into your
~/.bashrc
, you may want to improve it: check if the shell is insidetmux
before changing the behavior of Ctrl+D, so shells outside oftmux
are unaffected. - It's important that
_tmux_detach
acts beforedelete-char
. If you reverse the order then you will introduce a bug: Ctrl+D that deletes a solitary character will also detachtmux
.
No comments:
Post a Comment