Skip to content

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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. 😁

_An xclip banner, with the "X" swapped with a pair of scissors (via OpenClipArt)
An xclip banner, with the "X" swapped with a pair of scissors (via OpenClipArt)

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.

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):

%A QR code
A QR code

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
%Another example QR code (Do not listen to it, whatever it says.)
Another example QR code (Do not listen to it, whatever it says.)

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:

_Slide showing the text from above in two paragraphs, one with a forceful break
Slide showing the text from above in two paragraphs, one with a forceful break

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

_Slide showing the text from above in four paragraphs, one empty
Slide showing the text from above in four paragraphs, one empty

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.


  1. Others, e.g. Simon Tatham prefer to save commands outside of shell history, but I disagree. 😅↩︎