Pipelines, GUIs, clipboards, and CLIs
Usually, when you work on a computer, you use a variety of applications, each specific to part of the task. For example, you might use a spreadsheet to do some data wrangling and calculations, then copy that into a word processor for formatting the data as part of a larger essay. Or perhaps, you might be using an image editor to make cover art for a music file, that you then combine in a third application. Or perhaps, you might be using websites like clap.sh to transform your text before pasting it into a chat.
It is the same when you work with command-line applications instead of graphical applications. There is a few commands (applications) which let you talk with websites (e.g. curl
or wget
), others that let you evaluate mathematical expressions (well, many do; bash
, dc
/bc
, python
/node
/perl
/etc.), others lets you edit text, or perhaps read and write text files, some let you read/write compressed files (gzip
, tar
, zip
, 7z
, ...), there's command-line applications for editing images (imagemagick
) and videos (ffmpeg
, melt
), and so on.
In both cases, it is the operating system that lets you link applications together so that you can use multiple applications for one task. In the case of GUI applications, this is primarily handled through the clipboard—the space where data goes between copying it in one application and pasting it into another. And in the case of CLI applications, at least on Linux, this is handled primarily by "pipes"—a feature of the command-line shell that lets you take the output of one command and make it the input of another.
Example of using pipes
For example, here is how you might use pipes to calculate the sum of a list of numbers:
echo 1 9 2 4 10
# Prints: 1 9 2 4 10
echo 1 9 2 4 10 | sed 's/ /+/g'
# Prints: 1+9+2+4+10
echo 1 9 2 4 10 | sed 's/ /+/g' | bc
# Prints: 26
But how can you bridge the two? How can you get data from the GUI world and use it in the CLI world, or vice-versa? After all, the more applications you have available for a task, the more likely it is that you can combine a few of them to get it done: and sometimes, you really want to run a quick CLI script between the GUI applications you use, or a quick GUI application between the CLI commands you need.
There are a few ways you might do that:
- You could save things to a file. Both GUI applications and CLI applications can work files—especially text files—so files are a way to pass data between the two. But, doing so can be a hassle, as you need to e.g. both save a file and run the CLI application.
- You could copy and paste text from/to a terminal. However, if your data is not text, this is unlikely to work well. And also, you have to fiddle around with using
Ctrl-D
to terminate textual inputs. - You could use tools like AutoHotKey or
xdotool
to automate the pressing of buttons in a GUI application, as a kind-of recorded macro. However, doing that is extremely messy, and can break for a lot of mundane reasons, like a program starting just a bit too late or a software upgrade moving buttons around. - You could use
xclip
to pass data from the clipboard into a pipe or from a pipe into the clipboard.
That last option, xclip
is my favorite command, and the whole point of this blog post. 😁
What does xclip
do?
Very simple: it reads or writes data from/to the clipboard, from the command line.
Here is an example of how it works, when I use it to count words:
# Step 1: copy some text, e.g. this article
# Step 2: type out the following in a terminal (from memory or from history)
xclip -o -sel clip | wc -w
# Step 3: the command above has printed out the the number of words in your clipboard.
# Huzzah for not needing to fire up a word processor!
Here, the -o
option for xclip
tells it to output the copied text, -sel clip
tells it to use the main clipboard (otherwise it defaults to the most recently selected text), and wc
is a separate command for counting words (-w
telling it to output just the word count, and not a list of lines / words / characters).
Where can one get xclip
?
Well, if you are on Linux or Mac, you should check out your package manager for either astrand's original xclip if you are on X11, or brunelli's wl-clipboard-x11 and/or bugaevc's wl-clipboard if you are on Wayland.
(NOTE: Technically, wl-clipboard-x11
has been deprecated, and wl-paste
and wl-copy
are the recommended commands to use under Wayland. Yet, I have way too much muscle memory using xclip
, so the rest of this post will keep using xclip
, and I'll edit it once I've ported myself over to wl-{copy,paste}
😅)
Meanwhile, if you are on Windows, you might have to investigate PowerShell's Get-Clipboard and Set-Clipboard cmdlets instead. However, as I'm neither a Windows nor a PowerShell user, this is left as an exercise for the reader.
Why do I like xclip
so much?
xclip
is a fundamental part of my workflow. The word-counting pipeline above is just an example of it; I often pair it with commands like xargs
that let me do something for every line of text I have copied, with commands like sed
to do find and replace of some part of what I've copied, or perhaps with commands like sort
that let me, well, sort it.
In addition, xclip
pairs nicely with the shell I use, fish
, since it auto-completes commands from history, and thus I don't need to paste long command lines into my shell.1
Usually, developers use features provided by their text editor for a lot of what I use xargs
for. After all, a text editor lets you paste text, and when you pair that with the right plugins, you can search, replace, sort lines, execute commands, evaluate code, and so on. However, using xargs
gives me at least two benefits over using a fancy IDE: I get to use the whole array of Unix shell scripting tools, and I am not dependent on a particular IDE continuing to work.
Examples of using xclip
Counting words
Already seen above, but it is honestly one of the commands I use the most. 😂
xclip -o -sel clip | wc -w
Sorting lines
xclip -o -sel clip | sort | xclip -sel clip
This example demonstrates replacing the text in the clipboard, by piping the last command's result back into it. (Since, usually I end up pasting the sorted lines over the original lines that I've copied.)
Shuffling is also trivial, thanks to shuf
:
xclip -o -sel clip | shuf | xclip -sel clip
Inspecting bytes
xclip -o -sel clip | xxd
This produces a result which looks like the following:
00000000: 7863 6c69 7020 2d6f 202d 7365 6c20 636c xclip -o -sel cl
00000010: 6970 207c 2078 7864 ip | xxd
Pretty-print JSON
With the nowadays-popular jq
tool:
xclip -o -sel clip | jq
# Or, to copy it back once finished:
xclip -o -sel clip | jq | xclip -sel clip
# Or, to inspect part of the JSON
xclip -o -sel clip | jq '.field'
Make fancy ASCII-art texts
With the incomparable figlet
tool:
xclip -o -sel clip | figlet | xclip -sel clip
Example result:
_ _ _ _ _ _ __ _ _ _ _ _ _ _ _ _
__ _____| (_)_ __ ___ ___ ___| | ___| (_)_ __ | | / _(_) __ _| | ___| |_ | | __ _____| (_)_ __ ___ ___| | ___| (_)_ __
\ \/ / __| | | '_ \ _____ / _ \ _____/ __|/ _ \ | / __| | | '_ \ | | | |_| |/ _` | |/ _ \ __| | | \ \/ / __| | | '_ \ _____/ __|/ _ \ | / __| | | '_ \
> < (__| | | |_) | |_____| (_) | |_____\__ \ __/ | | (__| | | |_) | | | | _| | (_| | | __/ |_ | | > < (__| | | |_) | |_____\__ \ __/ | | (__| | | |_) |
/_/\_\___|_|_| .__/ \___/ |___/\___|_| \___|_|_| .__/ | | |_| |_|\__, |_|\___|\__| | | /_/\_\___|_|_| .__/ |___/\___|_| \___|_|_| .__/
|_| |_| |_| |___/ |_| |_| |_|
Naturally, there are websites that do stuff like that, but it's more fun (and less of a privacy issue) to do it locally, in a terminal.
Opening a list of links
If I have a list of links that I want to open, but they are not nicely organized inside a bookmarks folder in my browser, I can just use xargs
with the browser:
E.g., if I have a list of links like this:
https://bojidar-bg.dev/blog/2025-05-09-toolbox/
https://bojidar-bg.dev/blog/2025-05-28-sharp-tools/
I can copy them and then run:
xclip -o -sel clip | xargs -n 1 -- xdg-open
# or, to use a specific browser:
xclip -o -sel clip | xargs -n 1 -- firefox
And in the end, I'll get all of the links open in a browser.
Note that the xdg-open
command would happily work with a list of paths to files too.
Dealing with base64
Rather than pasting Base64 JWT tokens and the like into a JavaScript console through atob("...")
, I can just use the base64
utility:
# Decode base64-encoded text
xclip -o -sel clip | base64 -d
# Decode base64-encoded text, and inspect the bytes
xclip -o -sel clip | base64 -d | xxd
# Note: You might need to pass --ignore-garbage to base64 if you have extra, non-base64 characters in it
Likewise, you could use the base64
utility to encode data, similar to JavaScript's btoa
:
xclip -o -sel clip | base64
# Encode PNG data from the clipboard (since otherwise you might encode the path to a copied file)
xclip -o -sel clip -t image/png | base64
In fact, you could even use that to create data:
URL-s for copied images!
xclip -o -sel clip -t image/png | base64 -w 0 | sed -E 's|^|data:image/png;base64,|' | xclip -sel clip
# or:
xclip -o -sel clip -t image/jpeg | base64 -w 0 | sed -E 's|^|data:image/jpeg;base64,|' | xclip -sel clip
Making a QR code
qrencode
, part of libqrencode is a command-line utility for making QR codes. Very useful!
# To open an image viewer
xclip -o -sel clip | qrencode -o /tmp/xx.png; xdg-open /tmp/xx.png
# To copy the image back to the clipboard
xclip -o -sel clip | qrencode -o - | xclip -sel clip
Example result (with -s 6
passed to qrencode so the result is a bit larger):

If you are feeling fancy, you could even combine this with the data URL example above to get this monster:
xclip -o -sel clip | qrencode -o - | base64 -w 0 | sed -E 's|^|data:image/png;base64,|' | xclip -sel clip
Preparing text for Google Slides
Whenever I have to deal with Google Slides, I have this snippet at the ready:
xclip -o -sel clip | sed -Eze 's/\n\n+/\n\n/g;s/\n/<br>/g;s/^|<br>\s*<br>/<p style="text-align:center;margin-top:0pt;margin-bottom:6pt;">/g' | xclip -sel clip -t text/html
It's a bit tricky to explain what's going on here; but the idea is that I have text which looks like this:
This is a paragraph
which is split into two lines
This is a second paragraph
I then want to take that text, and turn it into a slide which looks like this:

However, if I directly copy and paste the text into Google Slides, without using my script, I get the following:

So, this command line lets me automatically fix up the paragraphs, without manually having to go through and fix the lines I've pasted, and without having to wait for Google Slides to get their software to work nicer.
Note that the last -t text/html
option to xclip -sel clip
is important, as otherwise, pasting the result into Google Slides would just give you the raw HTML code for the paragraphs.
Also, note that a similar result could be achieved with Pandoc instead, e.g. with
xclip -o -sel clip | pandoc -f markdown+hard_line_breaks - | xclip -sel clip -t text/html
Copy a public key
Instead of opening SSH keys in a text editor to copy them, I usually just run the following:
cat .ssh/id_rsa.pub | xclip -sel clip
Note that this is an unnecessarily use of cat
, but I like the consistency with other commands.
Conclusion
In my experience, xclip
is a really useful command—and a major part of my toolbox. It lets me move data between the GUI world where a lot of modern (and "modern") applications live, and the CLI world where data processing scripts live.
I like to use so much, that typing out command lines that involve xclip
has become muscle memory to me. I've yet to switch to more modern alternatives like wl-paste
and wl-copy
, but the main point remains: While it would be hard to convert GUI applications into a form that is as easy to automate as a command line, it is possible to get some of the benefits of the CLI with a command that lets us bridge the gap and transport data between the two.
And xclip
is my favorite command for doing that. ✨
This has been my 10th post of #100DaysToOffload. This time around, I'm experimenting with having a richer introduction for people unfamiliar with the subject matter, rather than throwing everyone into the deep end.
Others, e.g. Simon Tatham prefer to save commands outside of shell history, but I disagree. 😅↩︎