When you're inside a devcontainer or an SSH session, copying something to your local clipboard can be surprisingly awkward.
Maybe it's a config file, an error message, or the output of a command. You want it on your machine, but the remote environment doesn't have access to your local X11 or Wayland session, so tools like xclip or wl-copy don't help.
OSC 52 solves this without any clipboard software on the remote machine. It's a terminal escape sequence that tells your terminal emulator to update its clipboard. Because the sequence travels through the normal terminal output stream, it can cross an SSH connection, or a devcontainer boundary, and reach the terminal running on your local machine.
# How the sequence works
An OSC 52 sequence for setting the clipboard looks like this:
ESC ] 52 ; c ; <base64> BEL Or, using shell escape notation:
\e]52;c;<base64>\a The pieces are:
\e]start an Operating System Command 52clipboard operation ctarget the system clipboard <base64>base64-encoded clipboard contents \aterminate the sequence
The c selector means the system clipboard. OSC
52 also defines other selections, for example p for the primary selection, but c is normally what you want. The payload is Base64-encoded
before it is placed in the escape sequence, so copying hello is conceptually just:
The important part is where the sequence is interpreted. The remote process doesn't access your local clipboard directly. It merely writes bytes to the terminal. Your local terminal emulator receives those bytes, recognizes OSC 52, decodes the payload, and updates the clipboard. That's why this works so nicely over SSH.
# A small shell function
You can turn this into a shell function:
osc52() {
local data
data=$(base64 | tr -d '\n')
printf '\e]52;c;%s\a' "$data"
}
Put it in your .bashrc, .zshrc, or wherever you keep shell helpers.
Then:
printf 'hello' | osc52
and hello appears in your local clipboard.
Why printf instead of echo?
An escape sequence is just a stream of characters written to the terminal. What makes it special is that it starts with the ESC control byte: when the terminal emulator sees that byte, it interprets what follows as a command (like OSC 52) instead of rendering it as text.
The \e in a string literal is just a backslash
followed by the letter e, not an actual
escape byte. Something has to convert it into the single ESC byte.
In bash, echo only does that if you pass -e; in zsh it does it by default; in dash or sh it ignores the
backslash entirely and sends the literal characters, so the
terminal sees no ESC byte and just prints the text.
printf always performs this conversion consistently,
which is why the function uses it.
Copying a file works too:
osc52 < /path/to/file.txt Or pipe any command output into it:
git diff | osc52 No X11 forwarding. No clipboard daemon on the server. No temporary file to transfer back to your machine.
Why tr -d '\n'?
GNU base64 wraps output at 76 characters, which
would break the OSC 52 sequence. Stripping the generated newlines keeps
the payload on one line across implementations.
# Inside tmux
Tmux adds another layer, though. It processes the output of everything running inside it and, depending on its configuration, may not forward OSC 52 sequences to the outer terminal.
Tmux understands OSC 52 itself, so clipboard behavior depends on its configuration and terminal capabilities.
Check the current setting with:
tmux show -s set-clipboard A typical configuration is:
set -s set-clipboard on
With set-clipboard set to on, applications running inside tmux can use
OSC 52 and tmux can propagate the clipboard update toward the outer
terminal.
There is also a separate tmux feature called passthrough. It allows certain terminal
escape sequences wrapped in tmux's passthrough mechanism to reach
the outer terminal and is controlled by allow-passthrough.
That's useful for applications that deliberately use tmux
passthrough, but it isn't the same mechanism as tmux's built-in OSC
52 clipboard handling. You don't normally need to enable passthrough
just to use set-clipboard.
# Keep the payload small
OSC 52 is best treated as a mechanism for copying relatively small amounts of text, not as a file-transfer protocol.
Implementations impose different limits, and terminal multiplexers can introduce limits of their own. Base64 also increases the size of the data by roughly one third.
So this is perfect:
cat config.yaml | osc52 This probably isn't:
cat huge-database-dump.sql | osc52 Know the size limits
There's no universal cap. xterm's documented max is roughly 100 KB
for the whole sequence, about 74 KB of text after base64 and
headers. Through tmux it drops to about 32 KB, since tmux
truncates anything bigger. For large files or binary data, use an
actual transfer mechanism such as scp or rsync.
# That's it
OSC 52 is one of those terminal features that's almost comically simple once you know it exists. A few bytes written to a terminal turn into real clipboard content on your machine, no matter how many hops sit in between.
Small feature. Surprisingly useful.