note: the original question referred to tmux
version 2.3, and I confirmed
all of this works with both that version, and 3.2a
For me, this has usually been caused by the tmux
session starting without the
DISPLAY
environment variable set, meaning xclip
doesn't know how to
communicate with the X Server. This AskUbuntu answer is more focused on SSH,
but it includes a helpful paragraph:
To check whether X11 forwarding is enabled, look at the value of the DISPLAY
environment variable: echo $DISPLAY
. You should see a value like
localhost:10
[...]. Note that if DISPLAY
isn't set, it's no use setting
it manually [...]
This is what it looks like in my local, non-SSH tmux
session:
$ echo "${DISPLAY:?}"
:0
If you get something like the following, it means the DISPLAY
variable isn't set:
$ echo "${DISPLAY:?}"
sh: 1: DISPLAY: parameter null or not set
It's better to figure out how to launch the tmux
session inside the X Server
session, but if you can launch a terminal inside the X Server session, it could
be useful to find out what the DISPLAY
variable is, and temporarily set it in
the tmux
session, to see if it works:
export DISPLAY=':0'
The Ubuntu Community Wiki has a brief and exhaustive introduction to shell
variables (it mentions bash
, but most of the info is
applicable to most shells, except csh
, tcsh
, and fish
, which are not
POSIX_compliant); it describes DISPLAY
near the end. Note that the export VARIABLE_NAME=value
syntax is part of the POSIX shell programming language,
and is very counter intuitive, even for those with experience in other
languages. This Stack Exchange question has some more explanations of how to
set environment variables.
You can usually start any command-line program inside an X Server session by
starting a terminal application, and then running the command-line program
using the shell in the terminal.
On GNOME this would look like:
- Login to your account
- Start Terminal
- Run
tmux new-session
as a command in Terminal (sometimes it connects to
an already-running session anyways)
Once started, tmux
will launch a shell with the DISPLAY
environment
variable set, and xclip
should work:
$ xclip -verbose -out
Connecting to X server.
Using UTF8_STRING.
You may see other things, and if your clipboard already contains content, it
will be echoed to the terminal. Try copying, highlighting, or selecting some
text before running this.
If instead it shows:
xclip: command not found
This means that xclip
isn't installed, or can't be found.
If xclip
is working, then you can try to set the command as a tmux
key
binding (running this will probably only work when already inside a tmux
session):
tmux bind-key -T prefix b run-shell "tmux show-buffer | xclip -se c -i > /dev/null"
running tmux list-keys | less
shows the default key bindings, and the
syntax for setting them in a configuration file
I explicitly added the keybinding to the prefix
table. Commands (or keyboard
shortcuts) in tmux
's prefix
table are run by first pressing a leading key
sequence, which is Ctrl+b by default, or C-b in tmux
's
syntax. Pressing Ctrl+b then b (C-b b) should
run the command. I'll use prefix to indicate that the prefix key
combination should be pressed.
And we do get an error. tmux
opens copy-mode with the content 'tmux show-buffer | xclip -se c -i > /dev/null' returned 1
.
If the command run by run-shell
prints any output to stdout, tmux
opens
copy-mode in the current pane, showing that output. If the shell command fails,
it also displays a line stating the return or exit code. By default,
prefix then q will exit copy-mode.
Many programs print errors to stderr instead of stdout, including tmux
's
show-buffer
, so the error information is lost. To capture it, we can leave
off the shell pipe, and change the shell redirection:
$ tmux show-buffer
no buffers
$ echo $? # prints 1 if the previous command failed
1
$ tmux run-shell "tmux show-buffer"
'tmux show-buffer' returned 1
$ tmux run-shell "tmux show-buffer 2>&1"
no buffers
'tmux show-buffer 2>&1' returned 1
This error message is returned because, when tmux
starts, by default there
are no buffers.
If there are any buffers, the following command will show those buffers and
their contents (by default, it's bound to prefix then =):
tmux choose-buffer
If there are no buffers, this command returns immediately with no output. If
there are any buffers, choose-buffer
presents a list containing all buffers,
ordered by the last time they were used. Pressing ↑ and
↓ will scroll through the list, pressing Enter will
paste the contents of the selected buffer to the current pane, and q
will exit.
To create a buffer, used the set-buffer
command:
$ tmux set-buffer "test content"
$ tmux save-buffer - && echo
test content
note: the && echo
is needed because the content of the buffer does not end
in a new-line
With at least one buffer, the show-buffer
command opens copy-mode with the
contents of the most recently set buffer.
Note as in the above shell session that the save-buffer
command will print
the contents of the most recently set buffer to a file, and if -
is given as
the filename, then it will print to stdout.
The original configuration should probably be*:
bind-key b run-shell "tmux save-buffer - | xclip -sel clip -i"
*I'm guessing that you're trying to copy something from tmux
to the
system clipboard.
Typically, text in tmux
is selected by entering copy-mode first. By default,
this is bound to prefix then [. With the default
configuration, tmux
uses Emacs-style keybindings in copy-mode. In tmux
version 2.3, these keybindings can be shown with tmux list-keys -temacs-copy
.
In versions greater than 2.3, the list-keys
command shows the keybindings in
all tables by default.
note: vi
-style keybindings can be used by replacing emacs-copy
with
vi-copy
, or copy-mode-vi
instead of copy-mode
, in all commands and
configurations
The selected text can be copied, both to the system clipboard and a tmux
buffer. In tmux
version 2.3, this is possible with something like:
bind-key -temacs-copy M-w copy-pipe "xclip -selection clipboard -in"
This would override the default behaviour of Alt+w in
copy-mode to pipe the selected text to xclip
, in addition to copying the
selected text into a tmux
buffer.
In versions greater than 2.3, the equivalent configuration would be:
bind-key -T copy-mode M-w send-keys -X copy-pipe-and-cancel "xclip -selection clipboard -in"
In both versions, the following would replace the default behaviour of
prefix then ] with creating a tmux
buffer with the
contents of the X server clipboard, and then pasting the most recent buffer:
bind-key -T prefix ']' run-shell 'tmux set-buffer "$(xclip -selection clipboard -out)"' \; paste-buffer
Note that xclip
can behave strangely in some circumstances when used like this.
Version 2.3 was released in 2016, so most package repositories should
have a newer version.
Either way, I would recommend using tmux-yank
, as it manages the task of
copying from tmux
very well.
As to debugging what is causing errors in tmux
configurations, in general,
there are a few things we can do to make that easier:
- Run the commands from the terminal inside a
tmux
session (if possible)
- Create a temporary configuration file
- Show messages for longer
Run the commands from the terminal inside a tmux
session (if possible)
Most commands that can be put in a configuration file, can also be run from
inside a tmux
session, from a command line, by prefixing them with tmux
.
Most shells will interpret certain characters like [
]
;
&
"
'
specially, so when running tmux
commands, take care to quote those characters
so that they are passed to tmux
. All of the commands given above will work
when prefixed with tmux
, without modification, as well as in a configuration
file.
Running the commands like this may not be possible, like when in copy-mode and
the shell session isn't available.
When it is possible, this can help diagnose what exactly is happening with each
part of a command.
Sometimes, the command can be run from tmux
's command prompt (by default
prefix then :). The command is entered just as in the
configuration file, and pressing Enter runs it.
So for instance, in copy-mode, pressing prefix then :
changes the status line to yellow with the content :
, followed by the cursor.
Typing a command like display-message test
and then pressing Enter
will set the content of the status line to test
. To instead exit, press the
terminal's interrupt key combination (default in most terminals is
Ctrl+c; stty -a
may show something like intr = ^C
).
note: see the section "Show messages for longer" to increase how long these
messages are shown in the status line
note: tmux
version 2.4 and above allow running copy-mode commands from the
command prompt, like send-keys -X begin-selection
. I do not know how to do
this in version earlier than 2.4.
Create a temporary configuration file
To help with debugging, we can add the potential configuration changes in a
file called tmux_debug.conf
:
# -q: do not return an error if the file doesn't exist
source-file -q /etc/tmux.conf
source-file -q ~/.tmux.conf
add any experimental configuration below here
and run tmux
with:
tmux -f ./tmux_debug.conf
When tmux
is started, by default it reads from two configuration files:
/etc/tmux.conf
and ~/.tmux.conf
. The -f
option instructs it to ignore
those, and load a custom file. Since we may rely on configuration in those two,
we source them if they exist. This will allow us to keep our current
configuration without making permanent changes to it.
We can also add a keybinding for quick reloading of the temporary configuration
file:
bind-key -T prefix C-r source-file ./tmux_debug.conf \; display-message "sourced configuration"
This will set the key sequence prefix then
Ctrl+r to make tmux
reload the tmux_debug.conf
file
in the directory in which tmux
was started, and then show the text sourced configuration
in tmux
's status line.
The previous command displays a message in the status line. By default, tmux
only shows messages in the status line for 750ms, which is a source of
frustration.
In the configuration file, adding the following line will cause the text to be
displayed until a key is pressed:
set -g display-time 0
note: the key press is sent to whatever is running in the pane or window
that just had focus