OSC 52

Copy from a devcontainer or SSH session to your local clipboard

Sep 18, 2026

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.

Share

Comments (0)