Copying text to the clipboard: tmux, WSL, Windows Terminal
tl;dr
if-shell -b 'test -n "$WSLENV"' 'bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel clip.exe'
if-shell -b 'test -n "$WSLENV"' 'bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel clip.exe'
I have been using the Windows Subsystem for Linux (WSL) for approximately 2 years and it has tremendously affected my daily workflow in the most positive of ways. Having access to a Unix shell and the Gnu core utilities opened up ways to process data and complete my engineering work that was not possible before.
With that said, since it is not the common environment that many of the
tools were developed in, certain tasks need some tweaking in order to
get working correctly. One of those things is copying text to the
clipboard from within tmux
.
tmux
has a built in ‘copy mode’. However, that support expects that an X
server is running in the background, which is not the case for most of
the distributions available through the WSL. Fortunately, newer versions
of tmux
have the ability to specify a command to pipe the data
through.
The command in tmux
is copy-pipe
(and variants) and has the
following syntax, check the man page for exact commands in your version.
copy-pipe <command> [<prefix>]
copy-pipe-no-clear <command> [<prefix>]
copy-pipe-and-cancel <command> [<prefix>]
If we were in a Linux distribution, we’d probably have xclip
or xsel
available for us. This is not the case in Windows. What we do have is
clip.exe
. This a Windows executable, but it does what we need: Take
data piped into it and copy it to the clipboard.
You can do this in cmd.exe
like
echo hello | clip.exe
You can run Windows executables in the WSL. So we’ll want to rebind the Enter key press when in copy mode (I use the vi copy mode bindings, if you are strange and want to use emacs mode, this would just be ‘copy-mode’ instead of ‘copy-mode-vi’). This looks like:
bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel clip.exe
That is a lot and it works, but there’s more to be done.
My tmux
configuration is shared between many computers that are
running various OS’s. At the time of writing, we have two work computers
running Windows, one Manjaro, and one Arch Linux. I don’t want to mess
us the default settings for my Linux systems.
Again, newer versions of tmux
have a way to handle that. There is the
if-shell
command that allows you to execute commands based on the exit
code of a shell command. So I wanted to test whether I was in a WSL
environment before running the new bind-key
command.
There are different ways to do this, but I did a printenv
to see if
there were any environment variables that were related to the WSL. It
turns out there was. There was an environment variable called WSLENV
,
and it doesn’t matter what it is or what it does, just that it was
there.
So we then use a test
command (the command used to branch should be
POSIX, so no Bash-isms) to check for this variable. The -n
checks for
a non-zero length string. Notice that you quote the entire commands.
This ends up looking like
if-shell -b 'test -n "$WSLENV"' 'bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel clip.exe'
On Windows, I have been using the Windows Terminal. I have liked it, and they are making great progress towards a 1.0 release.
One of the features they just recently implemented was mouse support.
So now, I can turn on the mouse functionality in tmux
with
set -g mouse on
and I can copy to clipboard using the mouse drag by just changing the
‘key’ from my previous bind-key
command to MouseDragEnd1Pane
.
So in all, it looked like:
if-shell -b 'test -n "$WSLENV"' 'bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel clip.exe'
if-shell -b 'test -n "$WSLENV"' 'bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel clip.exe'