✂️ 为本地和远程 tmux 会话提供剪贴板访问
Clipper is a macOS "launch agent" — or Linux daemon — that runs in the background providing a service that exposes the local clipboard, and (optionally) the capacity to display desktop notifications, to tmux sessions and other processes running both locally and remotely.
In the above example, you start off in a shell on your local machine; from there you use ssh to a remote machine, where:
vim (from a shell, inside tmux, which itself runs in a shell).vim, it is fed into the ~/.clipper.sock socket.RemoteForward connection, the yanked text arrives back to the Clipper process running on your local machine.Once everything is configured, all of this happens transparently; editing in vim on a remote machine feels exactly like doing it on your local machine. While the above example uses macOS, vim, and tmux, the same patterns apply to other platforms and tools.
brew install clipper # run this outside of a tmux session
~/.tmux.conf# Bind "Enter" in copy mode to both copy and forward to Clipper:
bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "nc localhost 8377"
# Or, if you are running on a platform where nc requires the `-N` switch (eg. Ubuntu):
bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "nc -N localhost 8377"
# Or, if you are running Clipper on a UNIX domain socket:
bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "nc -U ~/.clipper.sock"
# Or, if your version of netcat doesn't have socket support and you want to use socat:
bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "socat - UNIX-CLIENT:~/.clipper.sock"
nnoremap y :call system('nc localhost 8377', @0)
" Or, if you are running on a platform where nc requires the `-N` switch (eg. Ubuntu):
nnoremap y :call system('nc -N localhost 8377', @0)
" Or, if you are running Clipper on a UNIX domain socket:
nnoremap y :call system('nc -U ~/.clipper.sock', @0)
" Or, if your version of netcat doesn't have socket support and you want to use socat:
nnoremap y :call system('socat - UNIX-CLIENT:~/.clipper.sock', @0)
Alternatively, use the nvim-clipper or vim-clipper plug-ins.
~/.bash_profile, ~/.zshrc etc# Pipe anything into `clip` to forward it to Clipper
alias clip="nc localhost 8377"
" Or, if you are running on a platform where nc requires the `-N` switch (eg. Ubuntu):
alias clip="nc -N localhost 8377"
# Or, if you are running Clipper on a UNIX domain socket:
alias clip="nc -U ~/.clipper.sock"
# Or, if your version of netcat doesn't have socket support and you want to use socat:
alias clip="socat - UNIX-CLIENT:~/.clipper.sock"
~/.ssh/config# Forward Clipper connection to remote host
Host host.example.org
RemoteForward 8377 localhost:8377
# Or, if you are running Clipper on a UNIX domain socket:
Host host.example.org
RemoteForward /home/me/.clipper.sock /Users/me/.clipper.sock
You're running tmux, possibly on a remote machine via ssh, and want to copy something using tmux copy mode into your local system clipboard.
You can hold down Option and click to make a selection, bypassing tmux and allowing you to copy straight to the system clipboard, but that won't work if you're using vertical splits (because the selection will operate across the entire width of the terminal, crossing the splits) or if you want to grab more than what is currently visible.
As a workaround for the vertical split problem, you can hold down Option + Command to make a rectangular (non-contiguous) selection, but that will grab trailing whitespace as well, requiring you to manually clean it up later. Again, it won't work if you want to grab more than what is currently visible.
As a result, you often find yourself doing a tiresome sequence of:
:set paste):w /tmp/buff)ssh user@host cat /tmp/buff | pbcopy or similarmacOS comes with a pbcopy tool that allows you to get stuff into the clipboard from the command-line. xclip is an alternative that works on Linux. We've already seen this at work above. Basically, we can do things like echo foo | pbcopy to place "foo" in the system clipboard.
tmux has a few handy commands related to copy mode buffers, namely save-buffer, copy-pipe and copy-pipe-and-cancel, the availability of which depends on the version of tmux that you are running. With these, you can dump the contents of a buffer to standard out.
In theory, combining these elements, we can add something like this to our ~/.tmux.conf:
bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel pbcopy
or, in a version of tmux prior to 2.4 (which use vi-copy instead of copy-mode-vi, and copy-pipe instead of copy-pipe-and-cancel):
bind-key -t vi-copy Enter copy-pipe pbcopy
or, in an even older version of tmux prior to 1.8 (which don't have the copy-pipe-and-cancel or copy-pipe commands):
bind-key y run-shell "tmux save-buffer - | pbcopy"
In practice, this won't work on versions of macOS prior to 10.10 "Yosemite" or after 10.11 "El Capitan" — there was a brief lapse during those versions where it did work — because tmux uses the daemon(3) system call, which ends up putting it in a different execution context from which it cannot interact with the system clipboard. For (much) more detail, see:
One workaround comes in the form of the reattach-to-user-space tool available here:
This is a wrapper which allows you to launch a process and have it switch from the daemon execution context to the "user" context where access to the system clipboard is possible. The suggestion is that you can add a command like the following to your ~/.tmux.conf to make this occur transparently whenever you open a new pane or window:
set-option -g default-command "reattach-to-user-namespace -l zsh"
Despite the fact that the wrapper tool relies on an undocumented, private API, it is written quite defensively and appears to work pretty well. While this is a workable solution when running on the local machine — so workable in fact that it is baked into tmux itself in version 2.6 — we'll need something else if we want things to work transparently both locally and remotely, or on older versions of tmux. This is where Clipper comes in.
Clipper is a server process that you can run on your local machine. It will listen on the network or on a UNIX domain socket for connections, and place any content that it receives into the system clipboard.
It can be set to run automatically as a "launch agent" in the appropriate execution context, which means you don't have to worry about starting it, and it will still have access to the system clipboard despite being "daemon"-like.
Through the magic of ssh -R it is relatively straightforward to have a shared tmux configuration that you can use both locally and remotely and which will provide you with transparent access to the local system clipboard from both places.
For Homebrew users, install by running (outside of all tmux sessions):
brew install clipper # and follow the prompts...
Alternatively, if you have a working Go environment on your system you can do:
go get github.com/wincent/clipper
Finally, if you want to do things manually, you can clone from the authoritative Git repo and build manually (which again requires a working Go environment):
git clone git://git.wincent.dev/clipper.git
cd clipper
go build
If you plan to use Clipper as a launch agent you'll need to put it somewhere the system can find it (ie. at a location in the standard PATH, such as under /usr/local/bin/) and update the included property list file to specify the full path to the location where you installed the Clipper executable.
The following examples show how you would install the built Clipper executable to /usr/local/bin/ after cloning the repo and performing a build. It also shows how you would set up Clipper as a launch agent (on macOS) or systemd service (on Linux) and start it running:
sudo cp clipper /usr/local/bin
cp contrib/darwin/tcp-port/dev.wincent.clipper.plist ~/Library/LaunchAgents/
launchctl load -w -S Aqua ~/Library/LaunchAgents/dev.wincent.clipper.plist
Note that these commands may fail to do the right thing inside of a tmux session under macOS. Run launchctl from outside of tmux, otherwise Clipper may find itself in the wrong execution context. Similarly, when running manually, either run Clipper outside of tmux or use the aforementioned reattach-to-user-space as a wrapper.
sudo cp clipper /usr/local/bin
cp contrib/linux/systemd-service/clipper.service ~/.config/systemd/user
systemctl --user daemon-reload
systemctl --user enable clipper.service
systemctl --user start clipper.service
Alternatively, if you'd like to run Clipper manually, you can do so with:
./clipper [--address=IP_ADDR|UNIX_SOCKET] \
[--port=PORT] \
[--logfile=LOGFILE] \
[--config=CONFIG_FILE]
A Homebrew installation can be reversed with:
brew uninstall clipper
A manual launch agent installation can be reversed with the following (and as before, note that you should probably only run launchctl outside of a tmux session):
launchctl unload ~/Library/LaunchAgents/dev.wincent.clipper.plist
rm ~/Library/LaunchAgents/dev.wincent.clipper.plist
sudo rm /usr/local/bin/clipper
On Linux:
systemctl --user stop clipper.service
systemctl --user disable clipper.service
sudo rm /usr/local/bin/clipper
rm -r ~/.config/clipper
To kill a manually-launched instance of Clipper, just hit Control+C in the terminal where it is running.
As previously noted, Clipper supports a number of command line options, which you can see by running clipper -h:
…
The defaults shown above apply on macOS. Run clipper -h on Linux to see the defaults that apply there.
You can explicitly set these on the command line, or in the plist file if you are using Clipper as a launch agent. Clipper will also look for a configuration file in JSON format and read options from it. When --config/-c is not passed, Clipper searches the following locations, in order, and uses the first readable one:
$XDG_CONFIG_HOME/clipper/clipper.json~/.config/clipper/clipper.json~/.clipper.jsonThe following options are supported:
addressexecutableflagslogfileporthandlers (see handlers section below)Here is a sample config file:
{
"address": "~/.run/clipper.sock",
"logfile": "~/Library/Logs/clipper.log"
}
Note that explicit command line options — including options supplied via a plist — trump options read from a config file.
--addressSpecifies the address on which the Clipper da
暂无开放 Issues,或尚未同步最近议题。