cheat sheet

pbcopy & pbpaste

Pipe data into and out of the macOS pasteboard from the shell — multi-pasteboard targets, newline handling, shell aliases, and real-world copy-paste recipes.

pbcopy & pbpaste — macOS Clipboard CLI

What it is

pbcopy and pbpaste are macOS-only command-line utilities that read and write the system pasteboard — the same data store the Edit → Copy / Edit → Paste menu items use. They have shipped with macOS since 10.3 (Panther), live in /usr/bin, and are signed by Apple. The pair turns the clipboard into a first-class file in the shell's redirection model: anything that can be piped can be pasted, and anything on the clipboard can be filtered. Reach for them whenever you want to move text between the terminal and a GUI app, or wire the clipboard into a shell pipeline; on Linux the closest equivalents are xclip/xsel (X11) or wl-copy/wl-paste (Wayland).

Install

Both tools ship with macOS and cannot be installed separately — they live in /usr/bin/, signed by Apple, and are always available on a default install.

bash
which pbcopy pbpaste
# /usr/bin/pbcopy
# /usr/bin/pbpaste

# Sanity check — round-trip a string
echo "hello" | pbcopy && pbpaste

Output:

text
/usr/bin/pbcopy
/usr/bin/pbpaste
hello

Syntax

pbcopy consumes stdin and writes it to the pasteboard. pbpaste reads from the pasteboard and writes to stdout. Neither tool reads or writes files directly; combine them with shell redirection (<, >, |) when you want file or process I/O.

bash
COMMAND | pbcopy                 # COMMAND's stdout becomes clipboard
pbcopy < file                    # file content becomes clipboard

pbpaste                          # write clipboard to stdout
pbpaste | COMMAND                # pipe clipboard into a tool
pbpaste > file                   # save clipboard to a file

Output: (none — exits 0 on success)

Essential options

Both tools accept the same two options. The most important is -pboard, which selects which of macOS's four named pasteboards to operate on.

OptionMeaning
-pboard NAMETarget a specific pasteboard: general (default), ruler, find, font
-Prefer FORMATWhen the pasteboard has multiple representations, prefer txt, rtf, or ps
-helpPrint a one-line usage hint
bash
echo "highlight me" | pbcopy -pboard find    # populate Find pasteboard
pbpaste -pboard find                          # read the Find pasteboard
pbpaste -Prefer txt                           # prefer plain text over RTF

Output:

text
highlight me

The four pasteboards

macOS exposes four named pasteboards, each used by a different system convention. Most of the time you only touch general; the others are useful for niche automations like cross-app Find-and-Replace.

PasteboardConvention
generalDefault — Edit → Copy / Edit → Paste
findFind pasteboard — the last "Find what?" string. Cmd-E populates it from a selection
fontFont pasteboard — last copied font attributes
rulerRuler pasteboard — last copied paragraph/ruler settings
bash
# Copy a search term to the system-wide Find pasteboard
echo "TODO" | pbcopy -pboard find

# Then Cmd-G in any text app jumps to the next "TODO"

Output: (none — exits 0 on success)


pbcopy — copy to clipboard

pbcopy is the input side of the pasteboard. It writes everything it reads from stdin into the chosen pasteboard, in UTF-8 by default. The tool exits when stdin closes, so it pairs naturally with pipes and here-strings.

Basic copy idioms

The two most common shapes are pipe into pbcopy and redirect a file into pbcopy. Both terminate after reading their input.

bash
echo "literal text" | pbcopy             # pipe a string
date | pbcopy                            # pipe command output
pbcopy < ~/.ssh/id_ed25519.pub           # redirect from a file
cat report.txt | pbcopy                  # equivalent (one extra process)
printf "no trailing newline" | pbcopy    # printf for precise byte control

Output: (none — clipboard updated)

Copying to a non-default pasteboard

-pboard selects the target. The most useful non-default is find — once you populate it from the terminal, hitting Cmd-G in any cooperating GUI app (TextEdit, Safari, Pages) jumps to the next occurrence of that string without ever opening the Find dialog.

bash
echo "TODO" | pbcopy -pboard find
echo "FIXME" | pbcopy -pboard find       # overwrite

Output: (none — exits 0 on success)

Preserving (or stripping) trailing newlines

echo appends a newline; printf does not. This is the single most common source of surprises — echo "foo" | pbcopy puts foo\n on the clipboard, which is sometimes wrong (paste a username into a web form and you've submitted the form). Use printf or tr -d '\n' to suppress it.

bash
echo    "user@example.com" | pbcopy      # has trailing \n
printf  "user@example.com" | pbcopy      # exact bytes, no newline
echo -n "user@example.com" | pbcopy      # works in bash but not POSIX-portable

# Strip trailing newlines from any source
pbpaste | perl -pe 'chomp if eof' | pbcopy

Output: (none — exits 0 on success)

Binary data

pbcopy happily accepts non-UTF-8 input, but most GUI apps will only understand the clipboard if it carries a text encoding they recognise. Base64-encode binary data before copying, and base64-decode on paste.

bash
# Copy a PNG as base64 (decodable by the recipient)
base64 < screenshot.png | pbcopy

# Sometime later …
pbpaste | base64 -D > /tmp/restored.png

Output: (none — exits 0 on success)

File-content copy with a here-string

zsh and bash both support <<<, which copies a single argument with a trailing newline. Use it for tiny strings without spinning up echo.

bash
pbcopy <<< "https://example.com/$(date +%F)"
pbcopy <<< $'line one\nline two\nline three'

Output: (none — exits 0 on success)


pbpaste — paste from clipboard

pbpaste is the output side: it dumps the current pasteboard contents on stdout. It does not modify the pasteboard, so it is safe to call repeatedly. By default it returns the plain-text representation; use -Prefer to pick a different one.

Basic paste idioms

The two common shapes are pbpaste into a tool and pbpaste into a file. pbpaste makes no assumptions about formatting — what you put in is what you get out.

bash
pbpaste                              # print clipboard
pbpaste > url.txt                    # save to file
pbpaste | wc -l                      # count lines in clipboard
pbpaste | jq .                       # pretty-print pasted JSON
pbpaste | tr -d '\n'                 # paste, strip newlines

Output:

text
https://example.com/2026-05-25

Preferred representation

When data is copied from a rich-text source — Notes, Pages, Safari with formatting — the pasteboard carries multiple representations: plain text, RTF, and sometimes PDF or HTML. -Prefer picks the preferred one. The default is txt.

bash
pbpaste -Prefer txt        # plain text (default)
pbpaste -Prefer rtf        # raw RTF if present
pbpaste -Prefer ps         # PostScript if present (rare)

Output: (RTF source)

text
{\rtf1\ansi\ansicpg1252\cocoartf2761
\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
\paragraphd\pardirnatural

\f0\fs28 \cf0 Hello}

Reading a non-default pasteboard

-pboard mirrors pbcopy — read the find, font, or ruler pasteboards the same way you wrote to them.

bash
pbpaste -pboard find             # last copied Find string
pbpaste -pboard font             # last copied font attributes
pbpaste -pboard ruler            # last copied ruler/paragraph

Output:

text
TODO

Detecting an empty clipboard

pbpaste exits 0 even when the pasteboard is empty — it just prints nothing. Detect "empty" by inspecting the byte count.

bash
if [ -z "$(pbpaste)" ]; then
  echo "clipboard is empty"
fi

# Or with parameter expansion
n=$(pbpaste | wc -c | tr -d ' ')
echo "clipboard has $n byte(s)"

Output:

text
clipboard has 24 byte(s)

Pipelines and one-liners

The real power of pbcopy/pbpaste is in the middle of a pipeline — they make the clipboard interchangeable with any other stream. The general pattern is: something → filter → pbcopy on the way in, and pbpaste → filter → something on the way out.

bash
# Copy a JSON column out of a CSV
awk -F, 'NR>1 {print $3}' sales.csv | pbcopy

# Convert pasted JSON to YAML on the clipboard
pbpaste | yq -P '.' | pbcopy

# Lowercase the clipboard
pbpaste | tr '[:upper:]' '[:lower:]' | pbcopy

# Strip leading whitespace and trailing newlines
pbpaste | awk '{$1=$1};1' | perl -pe 'chomp if eof' | pbcopy

# Sort the clipboard lines, in place
pbpaste | sort | pbcopy

# Replace tabs with commas
pbpaste | tr '\t' ',' | pbcopy

Output: (none — clipboard updated)

Useful aliases and functions

A handful of short aliases save a lot of typing. Drop these in ~/.zshrc (or ~/.bashrc on older Macs) — they cover 90 % of clipboard workflow.

bash
# Copy the current path
alias cwd='pwd | tr -d "\n" | pbcopy'

# Copy the contents of a file
copy() { pbcopy < "$1"; }

# Paste the clipboard into a new file
paste-to() { pbpaste > "$1"; }

# Round-trip the clipboard through a filter
clipfilter() { pbpaste | "$@" | pbcopy; }

# JSON pretty-print on the clipboard (one-key formatter)
alias jqclip='pbpaste | jq . | pbcopy'

# URL-encode the clipboard
urlencode-clip() {
  pbpaste | python3 -c 'import sys,urllib.parse; \
print(urllib.parse.quote(sys.stdin.read()), end="")' | pbcopy
}

Output: (none — exits 0 on success)

Working with multiple lines

GUI apps usually paste exactly what the pasteboard contains, including embedded newlines. That matters when copying a multi-line snippet — most editors paste it verbatim, but a single-line input field strips it to the first line. Use tr to flatten when you need a one-line value.

bash
# Multi-line copy
cat <<'EOF' | pbcopy
line one
line two
line three
EOF

# Flatten to one line, comma-separated
cat <<'EOF' | tr '\n' ',' | sed 's/,$//' | pbcopy
alice
bob
carol
EOF

# Join with semicolons
printf 'a\nb\nc\n' | paste -sd';' - | pbcopy

Output:

text
alice,bob,carol
a;b;c

Common pitfalls

  1. Trailing newline pasted into a form submits itecho "value" | pbcopy always includes \n. Use printf "%s" "value" | pbcopy for exact-byte copies.
  2. Pasted JSON has CRLF line endings — clipboards from Windows apps often arrive with \r\n. Fix with pbpaste | tr -d '\r' | pbcopy.
  3. Rich text clobbers plain text — Notes and Mail put RTF on the pasteboard. pbpaste returns the plain-text representation by default; if you suspect RTF mis-rendering, run pbpaste -Prefer rtf | head to inspect.
  4. pbcopy over SSH does not work — the pasteboard lives on the local Mac. Use ssh remote 'cat file' | pbcopy from the local terminal instead.
  5. Empty clipboard returns successpbpaste exits 0 with no output. Guard scripts with [ -z "$(pbpaste)" ] && exit 1.
  6. pbpaste | grep returns nothing — the clipboard might lack a trailing newline, which makes grep skip the last line. Force one: pbpaste | sed -e '$a\'.
  7. Binary data corrupts on paste — most apps treat the clipboard as text. Base64-encode binary blobs before pbcopy and decode after pbpaste.
  8. Universal Clipboard delay — when iCloud Universal Clipboard is enabled, there's a brief sync window where iOS/iPadOS sees the old value. Add a 1-second sleep if a script relies on cross-device timing.

Sources

References consulted while writing this article. Links open in a new tab.

  • Apple Developer — pbcopy(1) man page — Authoritative flag list used while writing the options reference for pbcopy and pbpaste.
  • SS64 — pbcopy — Cross-version notes.

Real-world recipes

Copy your SSH public key

The single most common pbcopy use case — copy the key once, paste it into GitHub / a server's authorized_keys.

bash
pbcopy < ~/.ssh/id_ed25519.pub

Output: (none — exits 0 on success)

Copy current directory to clipboard

Drop this in ~/.zshrc and tap cwd after any cd to grab the path for pasting into a GUI.

bash
cwd() { pwd | tr -d '\n' | pbcopy; print "Copied: $(pwd)"; }

Output:

text
Copied: /Users/alice/code/jockey

Paste markdown table into a CSV

Pasted markdown tables from a web page can be converted to CSV in one pipeline — strip the leading pipes, replace inner pipes with commas, drop the separator row.

bash
pbpaste \
  | sed 's/^|//; s/|$//; s/[[:space:]]*|[[:space:]]*/,/g' \
  | sed '/^[[:space:]]*-/d' \
  | pbcopy

# Then in any spreadsheet: paste, choose "Split into columns" if needed

Output:

text
name,role,team
alice,engineer,platform
bob,designer,brand

Copy a CSV column

Use awk to extract a single column from a CSV file and put it on the clipboard, ready to paste into a spreadsheet's adjacent column.

bash
awk -F, 'NR>1 {print $3}' sales.csv | pbcopy

Output: (none — clipboard contains the third column, one value per line)

Capture command output and the command itself

Wrap a long-running command so the prompt, the command, and the output all end up on the clipboard — useful for sharing a terminal session with a teammate without screenshots. Call it as capture make build — the build runs normally and a clipboard copy is left behind.

bash
{
  echo "$ $*"
  "$@" 2>&1
} | tee >(pbcopy)

Output:

text
$ make build
clang -Wall -Werror src/main.c -o bin/app
build complete

Pretty-print JSON on the clipboard

When you copy a minified JSON blob from a browser DevTools panel, run a one-key formatter to replace it with the pretty version.

bash
alias jqclip='pbpaste | jq . | pbcopy && echo "[clipboard formatted]"'

Output:

text
[clipboard formatted]

Encode the clipboard for a URL

URL-encode the current clipboard contents so they paste safely into a ?q= query parameter.

bash
pbpaste \
  | python3 -c 'import sys,urllib.parse; \
print(urllib.parse.quote(sys.stdin.read().rstrip("\n")), end="")' \
  | pbcopy

Output: (none — clipboard updated)

Move file content to the clipboard, then back to a new file

Useful when you've selected text in a GUI editor and want to redirect it into a file without saving twice.

bash
copy()  { pbcopy   < "$1"; }
dump()  { pbpaste  > "$1"; }

copy ~/.zshrc
# … switch app, paste into chat …
dump ~/Desktop/zshrc-snapshot.txt

Output: (none — files written)

Cross-pasteboard transfer

Move whatever is on the Find pasteboard onto the general pasteboard, so Cmd-V pastes the last Find string.

bash
pbpaste -pboard find | pbcopy

Output: (none — exits 0 on success)

Build a one-shot screenshot uploader

Take a screenshot directly to the clipboard, then dump it to a file and open it. Combine screencapture -c with pbpaste's ability to extract image data.

bash
screencapture -ci                       # interactive region → clipboard
osascript -e 'tell application "System Events" to keystroke "v"' \
  </dev/null >/dev/null 2>&1 || true    # noop placeholder

# Save the clipboard image to a file (requires `pngpaste`)
brew install pngpaste                   # one-time
pngpaste ~/Desktop/screen-$(date +%F-%H%M%S).png
open ~/Desktop/screen-*.png

Output:

text
/Users/alice/Desktop/screen-2026-05-25-091452.png

Stream a file's tail into the clipboard for sharing

Useful for grabbing the last N lines of a log without scrolling — copy the tail, paste into chat.

bash
tail -n 200 /var/log/install.log | pbcopy

Output: (none — clipboard contains 200 lines of install.log)

Round-trip clipboard through an LLM CLI

Pipe the clipboard through a local AI tool (or any filter), put the response back on the clipboard, and notify when done.

bash
clipllm() {
  pbpaste | claude --print | pbcopy \
    && osascript -e 'display notification "Clipboard updated" with title "clipllm"'
}

Output: (none — clipboard updated, notification shown)

macOS keeps a single-slot pasteboard; there is no built-in history. Tools like Maccy, Paste, or Raycast Clipboard History layer history on top, and they all observe pbcopy writes — so anything you put on the clipboard from the terminal flows into your history app automatically.

pbcopy's sibling on Linux is wl-copy (Wayland) or xclip -selection clipboard (X11). A portable script can detect the platform with command -v pbcopy >/dev/null && CLIP=pbcopy || CLIP="xclip -selection clipboard" and use $CLIP thereafter.