Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
This sets up keybindings in tmux that allow you to copy/paste to/from your OS X clipboard from tmux running inside an SSH connection to a remote host. Partially borrowed from http://seancoates.com/blogs/remote-pbcopy

Local (OS X) Side

~/Library/LaunchAgents/pbcopy.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
     <key>Label</key>
     <string>localhost.pbcopy</string>
     <key>ProgramArguments</key>
     <array>
         <string>/usr/bin/pbcopy</string>
     </array>
     <key>inetdCompatibility</key>
     <dict>
          <key>Wait</key>
          <false/>
     </dict>
     <key>Sockets</key>
     <dict>
          <key>Listeners</key>
               <dict>
                    <key>SockServiceName</key>
                    <string>2224</string>
                    <key>SockNodeName</key>
                    <string>127.0.0.1</string>
               </dict>
     </dict>
</dict>
</plist>

~/Library/LaunchAgents/pbpaste.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
     <key>Label</key>
     <string>localhost.pbpaste</string>
     <key>ProgramArguments</key>
     <array>
         <string>/usr/bin/pbpaste</string>
     </array>
     <key>inetdCompatibility</key>
     <dict>
          <key>Wait</key>
          <false/>
     </dict>
     <key>Sockets</key>
     <dict>
          <key>Listeners</key>
               <dict>
                    <key>SockServiceName</key>
                    <string>2225</string>
                    <key>SockNodeName</key>
                    <string>127.0.0.1</string>
               </dict>
     </dict>
</dict>
</plist>

~/.ssh/config

Host myhost
    HostName 192.168.1.123
    User myname
    RemoteForward 2224 127.0.0.1:2224
    RemoteForward 2225 127.0.0.1:2225

After adding the PLists above, you'll have to run:

launchctl load ~/Library/LaunchAgents/pbcopy.plist
launchctl load ~/Library/LaunchAgents/pbpaste.plist

Remote (Linux) Side

~/.tmux.conf

if-shell 'test "$(uname)" = "Linux"' 'source ~/.tmux-linux.conf'

~/.tmux-linux.conf

bind C-c run "tmux save-buffer - | pbcopy-remote"
bind C-v run "tmux set-buffer $(pbpaste-remote); tmux paste-buffer"

~/bin/pbpaste-remote

#!/bin/sh
nc localhost 2225

~/bin/pbcopy-remote

#!/bin/sh
cat | nc -q1 localhost 2224

~/.vimrc

function! PropagatePasteBufferToOSX()
  let @n=getreg("*")
  call system('pbcopy-remote', @n)
  echo "done"
endfunction

function! PopulatePasteBufferFromOSX()
  let @+ = system('pbpaste-remote')
  echo "done"
endfunction

nnoremap <leader>6 :call PopulatePasteBufferFromOSX()<cr>
nnoremap <leader>7 :call PropagatePasteBufferToOSX()<cr>
@NHDaly

This comment has been minimized.

Copy link

@NHDaly NHDaly commented Aug 30, 2015

Love this! Do you know if this still works for you? I'm having trouble getting the launchd daemon to work on OSX 10.10.5

@mikepqr

This comment has been minimized.

Copy link

@mikepqr mikepqr commented Dec 7, 2015

@burke This all works for me perfectly except the final piece of the puzzle: ~/.vimrc. I had to change:

  let @n=getreg("*")

to

  let @n=getreg('"')

and

let @+ = system('pbpaste-remote')

to

let @' = system('pbpaste-remote')

Not sure if this is a vim revision issue. This change was necessary for me on vim 7.4.488.

@devm33

This comment has been minimized.

Copy link

@devm33 devm33 commented Jan 27, 2016

+1000 this is amazing. To follow up from @williamsmj I can confirm his changed work perfectly for modern vim because the default register is "

So if you just want to be able to do <leader>6p to paste from the osx buffer and conversely y<leader>5 to copy to the osx buffer use @" and getreg('"') See my vimrc for actual config

@devm33

This comment has been minimized.

Copy link

@devm33 devm33 commented Jan 30, 2016

Another thing to note is here is that you might want to put pbcopy-remote and pbpaste-remote in somewhere like /usr/bin/ or symlinked there depending on how you start tmux.

If you see something like pbcopy-remote returned error code 127 that means that tmux can't find the script on the path it was started with.

@artnikbrothers

This comment has been minimized.

Copy link

@artnikbrothers artnikbrothers commented Mar 21, 2016

I need to work on OS X (remote) server from Linux (local). Can you please help me to solve copy/paste problem in my case?

@loliee

This comment has been minimized.

Copy link

@loliee loliee commented Aug 2, 2016

Awesome ! Still working on OSX 10.11.6.

@arooni

This comment has been minimized.

Copy link

@arooni arooni commented Aug 17, 2016

Is there any reason why this wouldn't work across multiple, simultaneous SSH connections with different servers?

It looks like TMUX is working great, but I can't seem to get it working for vim. is yy supposed to be copying => directly into my mac clipboard?

@jondkinney

This comment has been minimized.

Copy link

@jondkinney jondkinney commented Oct 13, 2016

@arooni it won't do that without some additional configuration. It can also depend if you have any other plugins running that cannibalize vim's normal yank capabilities. Here's how I have it setup with yankring and using the unnamed clipboard in Vim 7.4.

if has("unix")
  let s:uname = system("uname")
  if s:uname == "Linux\n"
    " Remote Clipboard
    function! PropagatePasteBufferToOSX()
      let @n=getreg('"')

      call system('pbcopy-remote', @n)
      echo "done"
    endfunction

    function! PopulatePasteBufferFromOSX()
      let @" = system('pbpaste-remote')
      echo "done"
    endfunction

    nnoremap <leader>3 :call PopulatePasteBufferFromOSX()<cr>
    nnoremap <leader>2 :call PropagatePasteBufferToOSX()<cr>

    nnoremap yy yy:call PropagatePasteBufferToOSX()<cr>

    function! YRRunAfterMaps()
      nnoremap Y   :<C-U>YRYankCount 'y$'<CR> <bar> :call PropagatePasteBufferToOSX()<CR>
      vnoremap y   y:call PropagatePasteBufferToOSX()<CR>
    endfunction
  endif
endif
@arooni

This comment has been minimized.

Copy link

@arooni arooni commented Mar 9, 2017

@jondkinney this is awesome ! thanks so much!

question: any way to make your call to PropogatePasteBufferToOSX() asynchronous ? Because of the network delay, and because vim doesn't have background jobs, I notice a slight delay. I tried messing around with tpope's vim-dispatch but couldn't get it working.

thanks again!

@yan12125

This comment has been minimized.

Copy link

@yan12125 yan12125 commented Sep 13, 2017

That's an excellent tutorial. Here I'd like to post some notes: if you use git-master of neovim [1], you can configure the clipboard provider so that conventional shortcuts like "+p "+y works. For example:

    let g:clipboard = {
          \   'name': 'SSH_from_macOS',
          \   'copy': {
          \      '+': 'nc -c localhost 2224',
          \      '*': 'nc -c localhost 2224',
          \    },
          \   'paste': {
          \      '+': 'nc localhost 2225',
          \      '*': 'nc localhost 2225',
          \   },
          \   'cache_enabled': 0,
          \ }

[1] neovim/neovim#6030

@jclosure

This comment has been minimized.

Copy link

@jclosure jclosure commented Jul 29, 2019

@jclosure

This comment has been minimized.

Copy link

@jclosure jclosure commented Jul 29, 2019

Incidentally, this approach has problems with multibyte chars like, ü. Seem pbcopy encodes w/ MacRoman.

Example 1:
From Linux->Mac, I send:

Grüssen

In Mac, I get:

Grüssen

Example 2:
From Mac->Linux, I send:

Grüssen

In Linux, I get:

Gr\237ssen

Example 3:
From Linux->Mac->Linux, I send:

Grüssen

In the 2nd Linux box, I get:

Grüssen

My shells in all systems have:

LC_CTYPE=en_US.UTF-8
LANG=en_US.UTF-8

The Linux boxes are using UTF-8 and I think the Mac's pasteboard by default is using MacRoman encoding.

This seems to show that I'm right:
After sending a multibyte char from Linux->Mac, do:

pbpaste | textutil -convert txt -stdin -stdout -encoding 30 | pbcopy

You get:

Grüssen

I've tried doing this conversion in the plist ProgramArguments, but so far no luck. Wonder if anyone else has a better approach to normalize the encoding differences.

Thanks.

@jclosure

This comment has been minimized.

Copy link

@jclosure jclosure commented Jul 30, 2019

Alright, here's a workaround for the encoding issue. Note this makes the assumption that our clipboard server is speaking UTF-8.

pbcopy

	<string>localhost.pbcopy</string>
	<key>ProgramArguments</key>
	<array>
	    <string>/bin/sh</string>
	    <string>-c</string>
	    <string>LC_CTYPE=en_US.UTF-8 pbcopy</string>
	</array>

pbpaste

        <string>localhost.pbpaste</string>
	<key>ProgramArguments</key>
	<array>
	    <string>/bin/sh</string>
	    <string>-c</string>
	    <string>LC_CTYPE=en_US.UTF-8 pbpaste</string>
	</array>

IANA character sets

FYI, you can always debug this using a local file, e.g.:

pbcopy cmd

/bin/sh -c "cat /dev/stdin > /tmp/clipboard.txt; cat /tmp/clipboard.txt | pbcopy"

pbpaste cmd

/bin/sh -c "pbpaste > /tmp/clipboard.txt; cat /tmp/clipboard.txt"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment