Read XKCD in the terminal with some bash magic
Tags: linux, bash, showdev
Published: 2022-02-25 10:54:26

[XKCD](https://xkcd.com/) is probably the most popular webcomic with Devs. It only seems right that you can read it from the comfort of your terminal via a `xkcd` command. How can we pull that off?

I use [Kitty](https://sw.kovidgoyal.net/kitty/) as my terminal of choice. It is fast, feature-rich, and is cross-platform (Linux and macOS). The real game-changer is that it is one of the few terminal emulators that uses GPU rendering, and has its own [graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/) for displaying images. So, out of the box Kitty can show images (with a caveat), why not put this capability to use?

## TLDR, behold the script 🐥

```bash
#!/usr/bin/env bash

# Save this file as "xkcd"

URL=https://xkcd.com/ 

if [ $# -gt 0 ] && [ $1 = "-r" ]
then
 URL=https://c.xkcd.com/random/comic
fi

img=$(curl -s -L $URL | 
grep 'href= "https://imgs.xkcd.com/comics.*' | 
cut -d> -f2 | 
cut -d< -f1)

kitty +kitten icat $img
```

Run `xkcd` to fetch the latest comic, or `xkcd -r` for a random selection.

For a more polished version, you can clone the GitHub repo: <https://github.com/robole/xkcd>.

Read on if you'd like to understand the finer details!

## Displaying images in the terminal

Kitty shows images via the `icat` kitten which is included in the default installation. A [kitten](https://sw.kovidgoyal.net/kitty/kittens_intro/#kittens) is a python script to extend the functionality of Kitty.

To display an image, you can run the following command in Kitty:

```bash
kitty +kitten icat img.png
```

**There is a caveat that [ImageMagick](https://www.imagemagick.org/) must be installed for `icat` to work.** I had it installed already, so everything worked first time for me! It supports all image types supported by ImageMagick.

I believe [iTerm2](https://iterm2.com/index.html) has similar functionality with its [`imgcat` script](https://iterm2.com/documentation-images.html).

If you want to use another terminal emulator, *maybe* you can use a python library called [Überzug](https://github.com/seebye/ueberzug) to achieve the same outcome. You could use one of the image viewer utilities, [feh](https://man.archlinux.org/man/feh.1) and [sxiv](https://man.archlinux.org/man/sxiv.1), but they will always open a new terminal window AFAIK. Personally, I prefer to have the comic shown as an inline image, and Kitty does that with the least fuss. It's a no-brainer for my personal preferences!

## Scripting time!

You should not need to install anything else if you have a *nix machine or have [cygwin](https://www.cygwin.com/) installed on Windows (its installed with [Git](https://git-scm.com/) usually). With the GNU coreutils: [`curl`](https://curl.se/), [`grep`](https://www.man7.org/linux/man-pages/man1/grep.1.html), and [`cut`](https://man7.org/linux/man-pages/man1/cut.1.html), we should be able to get the latest webcomic from the XKCD homepage.

<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqpw99rnsv3t48t9nzm1.png" alt="xkcd homepage" width="754" height="847" loading="lazy"/>

It would be nice to be able to read a random XKCD also. Is there a way to do this from the XKCD website?

We are in luck because the XKCD website has a [Random](https://c.xkcd.com/random/comic/) page where they fetch a random comic from their archive for you! So, we should be the able to do the same thing with the random page and home page!

To grab the homepage, we use `curl` with the "-s" flag (to run in silent mode - so there is no progress meter or error messages):

```bash
curl -s https://xkcd.com/
```

We can use `grep` to pick out the URL of the image of the webcomic. The webpage is quite short, so we wont have to write anything too gnarly. We just want to uniquely identify the image URL with the simplest [regex (regular expression)](https://en.wikipedia.org/wiki/Regular_expression) possible. On inspection of the HTML, it looks like this snippet is the easiest to target:

```html
Image URL (for hotlinking/embedding):
<a href= "https://imgs.xkcd.com/comics/rounding.png">https://imgs.xkcd.com/comics/rounding.png</a>
```

There is an extra space after the equals sign for the `href` attribute (oddly). If we target the `href` attribute with the beginning of the URL, we will will get this as the only result. The regex `href= "https://imgs.xkcd.com/comics.*` should do the trick. We pipe the output of the `curl` command to the `grep` command and get the snippet we are after.

<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zzrzhlr6vg9xp2qhvc0l.png" alt="using grep to find the link in the webpage" width="935" height="162" loading="lazy"/>

Next, we want to get the URL on its own. We can use `cut` to strip out the unwanted parts of this string using the angled brackets as a delimiter. Using `cut -d> -f2` on the output gives us "https://imgs.xkcd.com/comics/rounding.png</a", if we pipe this output to `cut -d< -f1`, we get the URL on its own!

So, as a single line command, we can run the following the latest webcomic:

```bash
kitty +kitten icat $(curl -s https://xkcd.com | 
grep 'href= "https://imgs.xkcd.com/comics.*' | 
cut -d> -f2 | 
cut -d< -f1)
```

<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w8jbksoigfaqn88me4b4.png" alt="complete command to fetch XKCD image from homepage" width="935" height="486" loading="lazy"/>

Lets tidy the script up to make it more readable, and add the ability to choose a random comic with a `-r` flag.

If there is a "-r" argument passed as the first parameter to the script, we will fetch the page <https://c.xkcd.com/random/comic> instead of the homepage. Since this URL will redirect you to another page, we need to adjust our `curl` command to follow this redirect. We can do this by adding the "-L" flag. And thats it, the rest of the logic is the same for getting the correct image from the fetched page.

This is our final script, which we put in a file named "xkcd":

```bash
#!/usr/bin/env bash

URL=https://xkcd.com/ 

if [ $# -gt 0 ] && [ $1 = "-r" ]
then
 URL=https://c.xkcd.com/random/comic
fi

img=$(curl -s -L $URL | 
grep 'href= "https://imgs.xkcd.com/comics.*' | 
cut -d> -f2 | 
cut -d< -f1)

kitty +kitten icat $img
```

Now, to get a random comic, we can run `xkcd -r`.

<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dxopkhwsxqt2e67rgidw.png" alt="using script to fetch random XKCD comic" width="880" height="248" loading="lazy"/>

Mission accomplished!

## Final word

You got to love the power of bash scripting for this type of task. Our basic functionality can be achieved with a single-line command, but in less than a dozen lines, we have made a tidy script for reading our favourite comic on the command-line. 🤓

---
**Thank you for reading! Feel free to subscribe to my [RSS feed](https://www.roboleary.net/feed.xml), and share this article with others on social media. 💌**

**You can show your appreciation by buying me a coffee on [ko-fi](https://ko-fi.com/roboleary). 🙂**