This article explains how to get a working clipboard integration to the local machine when connecting over ssh.
When connecting over ssh, the remote host can’t post updates to the local clipboard, which makes transferring a piece of text from one connected machine to the other a pain of using the mouse and if tmux or such is being involved, then even worse.
Having a fifo file on the remote host through which we can yank whatever text we’d like to the local host, by also listening to the fifo from it using an additional connection.
It doesn’t require anything from the remote machine - no installation, no special packages.
Paste the following into your bashrc and use sshx host
to connect.
On the remote machine echo SOMETHING > ~/clip
and hopefully, SOMETHING will end up in the local host’s clipboard.
You will need the xclip
utility on your local host.
_dt_term_socket_ssh() {
ssh -oControlPath=$1 -O exit DUMMY_HOST
}
function sshx {
local t=$(mktemp -u --tmpdir ssh.sock.XXXXXXXXXX)
local f="~/clip"
ssh -f -oControlMaster=yes -oControlPath=$t $@ tail\ -f\ /dev/null || return 1
ssh -S$t DUMMY_HOST "bash -c 'if ! [ -p $f ]; then mkfifo $f; fi'" \
|| { _dt_term_socket_ssh $t; return 1; }
(
set -e
set -o pipefail
while [ 1 ]; do
ssh -S$t -tt DUMMY_HOST "cat $f" 2>/dev/null | xclip -selection clipboard
done &
)
ssh -S$t DUMMY_HOST \
|| { _dt_term_socket_ssh $t; return 1; }
ssh -S$t DUMMY_HOST "command rm $f"
_dt_term_socket_ssh $t
}
This solution is relying heavily on an ssh feature called ControlMaster. In short, this feature allows to piggy back several ssh connections on a single actual tcp one. Here we use it to open an additional connection that waits on a fifo file for clipboard content.
Let’s break it down.
local t=$(mktemp -u --tmpdir ssh.sock.XXXXXXXXXX)
this generates a temporary file name in the ssh.sock.*
pattern in the /tmp
directory (probably, depends on your system).
local f="~/clip"
is the remote path for the fifo file representing the clipboard.
ssh -f -oControlMaster=yes -oControlPath=$t $@ tail\ -f\ /dev/null || return 1
create a new master connection and hold it with the tail -f /dev/null
command. This will also create the socket file through which we’ll make the additional connections.
ssh -S$t DUMMY_HOST "bash -c 'if ! [ -p $f ]; then mkfifo $f; fi'" \
|| { _dt_term_socket_ssh $t; return 1; }
use the existing socket to create the fifo file. It will fail if it already exists but is not a fifo file - and that is on purpose. We’re ssh’ing to DUMMY_HOST because when a socket is specified with the -S
parameter, it doesn’t matter which host you mention on the command line. If there is a failure, we’ll call the _dt_term_socket_ssh
function:
_dt_term_socket_ssh() {
ssh -oControlPath=$1 -O exit DUMMY_HOST
}
and this is an important one: when we call the socket with -O exit
, it terminates all of the connections going through it.
(
set -e
set -o pipefail
while [ 1 ]; do
ssh -S$t -tt DUMMY_HOST "cat $f" 2>/dev/null | xclip -selection clipboard
done &
)
this subshell is the clipboard listener. Thanks to the set -e
, when the exit call is received, the ssh process terminates with exit code 255 and terminates the loop and the subshell.
ssh -S$t DUMMY_HOST \
|| { _dt_term_socket_ssh $t; return 1; }
this is the actual session. When you logout and this blocking command terminates this will be the time to cleanup:
ssh -S$t DUMMY_HOST "command rm $f"
_dt_term_socket_ssh $t
After this, nothing is left - no fifo, no listener and no socket file on local machine.
A bonus snippet - a vim clipboard writer for the remote host:
function! s:DumpContToFile(reg_cont, fname)
if !filereadable(a:fname)
echoerr a:fname . " does not exist"
return
endif
execute "redir! > " . a:fname
silent echon a:reg_cont
redir END
echo "Dumped to remote clipboard"
endfunction
nnoremap <silent> <Space>c :call <SID>DumpContToFile(@", expand("~")."/clip")<CR>
Will basically write the default register to ~/clip
bound to <Space>c
And that’s it. Tell me what you think in a mail or something :)
Notes:
mux_master_process_new_session: tcgetattr: Inappropriate ioctl for device
, consider applying this patch.
There is nothing inherently wrong, this message just needs to be filtered.