Remote pbcopy
I use the command line a lot. I'm sure many of you do, too.
I find myself often piping things between processes:
$ cat seancoates.com-access_log \ > | awk {'print $1'} \ > | sort \ > | uniq \ > | wc -l 627 $ # unique IPs
One particularly useful tool on my Mac is the pbcopy
utility,
which takes standard input and puts it on the pasteboard
(this is known as the "clipboard" on some other systems). Its sister
application, pbpaste
is also useful (it outputs your pasteboard to
standard output when your pasteboard contains data that can be represented in
some sort of text form—if you have image data copied, for example,
pbpaste
yields no output).
$ cat seancoates.com-access_log \ > | awk {'print $1'} \ > | sort \ > | uniq \ > | pbcopy $ # the list of unique IPs is now on my pasteboard
I find this particularly useful for getting information from the command line into a GUI application.
Wouldn't it be even more useful if we could
pbcopy
from a remote SSH session? Indeed it is useful. Here's
how.
The first thing you need is a listener on your local machine. Luckily,
Apple has provided us with launchd
and its administration utility,
launchctl
. This is basically [x]inetd
for your Mac
(plus a bunch of other potentially great stuff that I simply don't understand).
Put the following in ~/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>
…then, run: launchctl load ~/Library/LaunchAgents/pbcopy.plist
This sets up a listener on localhost
(127.0.0.1
) port 2224
, and sends any data received on
this socket to /usr/bin/pbcopy
. You can try it with
telnet
:
$ telnet 127.0.0.1 2224 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. hello ^] telnet> ^D Connection closed.
…then try pasting. You should have hello
(followed by a
newline) on your pasteboard.
The next step is tying this into SSH. Add
RemoteForward 2224 127.0.0.1:2224
to ~/.ssh/config
.
This will tell your SSH connections to automatically forward the remote
machine's local port
2224
to your local machine, on the same port, over your encrypted
SSH tunnel. It's essentially the same thing as adding
-R2224:localhost:2224
to your SSH connection command.
Now you have a listener on your local machine, and a secure tunnel from remote servers to this listener. We need one more piece to tie everything together. Put the following in a file (preferably in your path) on the remote machine(s) where you'd like a pipe-friendly pasteboard:
cat | nc -q1 localhost 2224
…I like to put this in ~/bin/pbcopy
or
/usr/local/bin/pbcopy
on servers where I have root. You'll also
need to chmod +x
this file to make it executable. You'll need
the nc
executable, which is often available in a
package called netcat
. This invocation of nc
takes
standard input and pushes it to localhost
on port
2224
.
Now you should have a useful pbcopy
on your remote server(s).
Be aware, though, that there is no additional security on this port connection.
If someone on the remote machine can connect to localhost:2224
,
they can inject something into your pasteboard. This is usually safe, but you should
definitely keep it in mind. Also, if you have multiple users using this
technique on the same server, you'll probably want to change the port numbers
for each user.
I use this technique all the time. Now you can too. Hope it's helpful.