6

Using iTerm2, I start an interactive SSH session to a remote Linux machine (where pbcopy is not available). On the remote Linux machine I would like to capture the output of some arbitrary command (e.g. ls *.foo), and have it magically appear in my OSX clipboard, so that when I hit +v it appears in whatever OSX application I've just pasted into. Is this possible? I've tried the escape codes mentioned on the iTerm2 doc page and can't make them work.

  • @klanomath -- thanks, I'll consider that (I'm not sure I want to setup access back to my machine via SSH keys, though it could be passcode-protected, of course.). – Joe Casadonte Oct 17 '16 at 16:54
  • It does require X on the remote server, which isn't there, and XQuartz running on the local server, which makes my machine run hot. But it might be my best bet -- thanks! – Joe Casadonte Oct 17 '16 at 17:09
  • then nbren12's comment to the second answer! ;-) – klanomath Oct 17 '16 at 17:11
  • Looked at that already -- it still requires SSH keys be set up, and it's the only way I'd be able to do it, since one use case has me SSH'ing into the remote machine from a VPN connection, so no static IP or hostname. Another problem is that I have different user account names on my two primary local machines, but I could solve that in an environment variable or something. Thanks for the effort, though, I really appreciate it. – Joe Casadonte Oct 17 '16 at 17:16
4

Solution

Piecing together lots of information from several different sources, here's what I came up with.

Local Daemon

From the local computer (OSX), setup a daemon to listen on a specific port, via launchd (see links below). The daemon process will simply call pbcopy, which will take anything passed in via STDIN and put it on the clipboard. To do this you need to setup a launchd plist file. Mine looked like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>local.pbcopy.9999</string>
    <key>UserName</key>
    <string>joe</string>
    <key>Program</key>
    <string>/usr/bin/pbcopy</string>
    <key>StandardOutPath</key>
    <string>/tmp/pb9999.out</string>
    <key>StandardErrorPath</key>
    <string>/tmp/pb9999.err</string>
    <key>Sockets</key>
    <dict>
        <key>Listeners</key>
        <dict>
            <key>SockNodeName</key>
            <string>localhost</string>
            <key>SockServiceName</key>
            <string>9999</string>
        </dict>
    </dict>
    <key>inetdCompatibility</key>
    <dict>
        <key>Wait</key>
        <false/>
    </dict>
</dict>
</plist>

By convention, the plist file name should be the label name with .plist appended, so for the above example, it would be local.pbcopy.9999.plist. If you wish to use a port other than 9999, just change it everywhere (keeping in mind that it should be something above 1024 and should not be a well-known port that you might already be using). Once you have things working, you can remove the StandardOutPath and StandardErrorPath keys and strings, as they're only needed for debugging.

In order to load the daemon, run the following command:

$ launchctl load local.pbcopy.9999.plist

You can see that it's loaded or remove it with the following commands:

$ launchctl list local.pbcopy.9999
$ launchctl remove local.pbcopy.9999

If you would like this to load every time you login, place the plist file into the ~/Library/LaunchAgents directory.

Note: you will need to set this up on each local host you want this to work on.

SSH Port-Forwarding

Because I could be accessing the remote machine from several different local computers, I can't hard-code the sending of the data on the remote machine to a specific host. To make this as painless and dynamic as possible, I've used SSH port-forwarding to create a dynamic link from the remote machine back to the local computer (the how's and why's of this are beyond this answer; see below for more information). Specifically, I create a link from the remote machines's port 9997 to the local computer's port 9999, which now has a daemon listening on it, thanks to the launchd stuff above. I could use port 9999 on both the remote machine and the local computer, but I don't need to.

To setup this tunnel, execute the following command:

$ ssh -R 9997:localhost:9999 user@remote.com

You can remote into multiple different remote machines with the same command and all will function as expected. You can remote into the same remote machine multiple times with the same command, and it will sort of work as expected; see note below.

If you don't feel like typing the -R 9997:localhost:9999 on every SSH invocation you make, you can put the remote forwarding definition in the SSH config file to do it automatically. Here's an example from my ~/.ssh/config file:

Host ufo*
  RemoteForward 9997 localhost:9999

With that, any time that I SSH to a host whose name starts with 'ufo', the remote forwarding from 9997 to localhost:9999 will automatically be set up. See the config file man page link below for more options.

Sending Data

On the remote end I use netcat to send the desired content back to the listening daemon.

$ date | nc localhost 9997

You can get as complicated as you'd like:

$ nc localhost 9997 <<EOF
> `ls -ld *`
> `date`
> EOF

You can even dynamically decide whether or not to send any data, based on whether or not anyone is listening (there's probably a more efficient way to do this, but it works):

#!/bin/bash
cnt=`(netstat -lnptu 2>/dev/null) | grep 127.0.0.1:9999 | grep -v grep | wc -l`
if [[ $cnt -eq 1 ]]; then
    date | nc localhost 9999
fi

Observations & Caveats

Things I noted:

  • pbcopy plays nicely with Copy'em Paste
  • if you initiate more than one SSH session with the same remote port forwarding to the same remote machine, only the first one will have any effect; no "duplicate port" type errors will be reported on either end
  • similarly, if you remote in from several different local computers to the same remote machine using the same remote port (e.g. 9997), only the first one will have any effect

Reference Links

  • 1
    If you don't havenc on your remote host, this works--at least on RHEL: echo "howdy" > /dev/tcp/localhost/9997 – mellow-yellow Dec 7 '18 at 18:47
  • I have used this solution for more than 1 year, recently noticed that it does not support unicode. For example, clipboard was filled with test‚ûú abc after echo "test➜ abc"|nc -w 0 localhost 9999, which means is converted into ‚ûú in the progress, anyone has an idea about this? – brook hong Oct 17 at 10:02
2

remote pbcopy for iTerm2 seems to be a piece of software made especially for this problem.

0

You can do this

ssh -ttt user@location "ksh -c 'ls *'" | pbcopy
  • I need a solution for an interactive shell session -- I'll clarify above. – Joe Casadonte Oct 17 '16 at 13:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .