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.
which pbcopy pbpaste
# /usr/bin/pbcopy
# /usr/bin/pbpaste
# Sanity check — round-trip a string
echo "hello" | pbcopy && pbpaste
Output:
/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.
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.
| Option | Meaning |
|---|---|
-pboard NAME | Target a specific pasteboard: general (default), ruler, find, font |
-Prefer FORMAT | When the pasteboard has multiple representations, prefer txt, rtf, or ps |
-help | Print a one-line usage hint |
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:
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.
| Pasteboard | Convention |
|---|---|
general | Default — Edit → Copy / Edit → Paste |
find | Find pasteboard — the last "Find what?" string. Cmd-E populates it from a selection |
font | Font pasteboard — last copied font attributes |
ruler | Ruler pasteboard — last copied paragraph/ruler settings |
# 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.
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.
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.
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.
# 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.
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.
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:
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.
pbpaste -Prefer txt # plain text (default)
pbpaste -Prefer rtf # raw RTF if present
pbpaste -Prefer ps # PostScript if present (rare)
Output: (RTF source)
{\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.
pbpaste -pboard find # last copied Find string
pbpaste -pboard font # last copied font attributes
pbpaste -pboard ruler # last copied ruler/paragraph
Output:
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.
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:
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.
# 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.
# 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.
# 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:
alice,bob,carol
a;b;c
Common pitfalls
- Trailing newline pasted into a form submits it —
echo "value" | pbcopyalways includes\n. Useprintf "%s" "value" | pbcopyfor exact-byte copies. - Pasted JSON has CRLF line endings — clipboards from Windows apps often arrive with
\r\n. Fix withpbpaste | tr -d '\r' | pbcopy. - Rich text clobbers plain text — Notes and Mail put RTF on the pasteboard.
pbpastereturns the plain-text representation by default; if you suspect RTF mis-rendering, runpbpaste -Prefer rtf | headto inspect. pbcopyover SSH does not work — the pasteboard lives on the local Mac. Usessh remote 'cat file' | pbcopyfrom the local terminal instead.- Empty clipboard returns success —
pbpasteexits 0 with no output. Guard scripts with[ -z "$(pbpaste)" ] && exit 1. pbpaste | grepreturns nothing — the clipboard might lack a trailing newline, which makesgrepskip the last line. Force one:pbpaste | sed -e '$a\'.- Binary data corrupts on paste — most apps treat the clipboard as text. Base64-encode binary blobs before
pbcopyand decode afterpbpaste. - 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
sleepif 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
pbcopyandpbpaste. - 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.
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.
cwd() { pwd | tr -d '\n' | pbcopy; print "Copied: $(pwd)"; }
Output:
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.
pbpaste \
| sed 's/^|//; s/|$//; s/[[:space:]]*|[[:space:]]*/,/g' \
| sed '/^[[:space:]]*-/d' \
| pbcopy
# Then in any spreadsheet: paste, choose "Split into columns" if needed
Output:
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.
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.
{
echo "$ $*"
"$@" 2>&1
} | tee >(pbcopy)
Output:
$ 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.
alias jqclip='pbpaste | jq . | pbcopy && echo "[clipboard formatted]"'
Output:
[clipboard formatted]
Encode the clipboard for a URL
URL-encode the current clipboard contents so they paste safely into a ?q= query parameter.
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.
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.
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.
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:
/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.
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.
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
pbcopywrites — so anything you put on the clipboard from the terminal flows into your history app automatically.
pbcopy's sibling on Linux iswl-copy(Wayland) orxclip -selection clipboard(X11). A portable script can detect the platform withcommand -v pbcopy >/dev/null && CLIP=pbcopy || CLIP="xclip -selection clipboard"and use$CLIPthereafter.