Save whatever is on the clipboard to a sensibly-named file.
You screenshot something, or copy a chunk of JSON, and now you want it on disk.
The usual routine is: open an editor, paste, think of a filename, pick a folder,
save. clip2file collapses that into one command — and picks the extension for
you by looking at what you actually copied.
$ clip2file
Saved image -> clip_2026-03-04_153045.png (284.1 KB)
$ clip2file
Saved json -> clip_2026-03-04_153112.json (1.2 KB)
$ clip2file
Saved text -> clip_2026-03-04_153140_meeting-notes-for-friday.txt (612 B)pip install clip2file # text only, zero dependencies
pip install "clip2file[image]" # adds image support via PillowPython 3.9+.
| Detected | Extension | How |
|---|---|---|
| Image | .png |
Clipboard bitmap, via Pillow |
| JSON | .json |
Actually parses it — malformed JSON stays .txt |
| CSV | .csv |
Consistent column count across 2+ lines |
| URL | .url.txt |
A single line that parses as a URL |
| Code | .txt |
Language keyword hints |
| Text | .txt |
Everything else |
Filenames are timestamped and, for text, get a short slug taken from the first line — so you can tell your clips apart at a glance without opening them.
clip2file # save to the current directory
clip2file -d ~/screenshots # save somewhere specific (created if missing)
clip2file -n notes.txt # use an exact filename
clip2file --text # ignore images, take text even if a bitmap is there
clip2file --no-describe # bare timestamp, no content hint in the name
clip2file --print # show what's on the clipboard, write nothing
clip2file -q # print only the path, for pipingNothing is ever overwritten by accident — a collision becomes note-2.txt,
note-3.txt and so on. Pass -f if you actually want to clobber.
Piping works how you'd expect:
code "$(clip2file -q)" # save the clip and open itThe clipboard sits behind a small protocol, so the detection and naming logic is usable and testable on its own:
from clip2file import ClipContent, ContentKind, detect_kind, save_clip
detect_kind('{"a": 1}') # ContentKind.JSON
detect_kind("a,b\n1,2") # ContentKind.CSV
class MyClipboard:
def read(self):
return ClipContent(kind=ContentKind.TEXT, text="hello")
result = save_clip(MyClipboard(), "./out")
print(result.path, result.size_bytes)Image support needs Pillow on every platform. Text works with no dependencies at all, falling back to the OS clipboard tool:
- Windows —
Get-Clipboard - macOS —
pbpaste - Linux —
wl-paste,xcliporxsel
pip install -e ".[dev]"
pytestThe tests fake the clipboard, so they run anywhere — no display server needed.
MIT