I was at Berlin Hack and Tell yesterday (it’s super cool, you should totally come by if you are in town).

The idea is basically that eight people each have five minutes to present their hacks (and the definition of hack is rather loose here), followed by five minutes of Q&A. It was kind of a slow night yesterday with few people wanting to present. Since it’s always nice to have eight hacks presented though I looked through my computer and found this one liner:

watch -t -n 8 'cowsay -f "$(ls /usr/local/share/cows | gshuf -n 1)" "$(curl https://icanhazdadjoke.com/ 2>/dev/null)"'

(note: if you are on macOS you might need to install some linuxy commands: brew install watch cowsay coreutils)

You an see it in action here:


Essentially it’s made out of 5 parts: watch, ls, gshuf, curl and cowsay.

It works like this:

watch is a command for running another command at a set interval. In my example I am telling it to run every eight seconds (-n 8) and not to show the name of the command when running (-t).

The code that it is running is cowsay. cowsay is a simple command line tool that just takes the input that you feed to it and returns a representation of a cow saying the words:

knut@Knuts-MBP ~> cowsay Hello!
< Hello! >
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

It takes a flag -f with which you can specify a different cowfile to use. It ships with some example cowfiles that live in /usr/loca/shares/cows. What I am doing in the command is listing all the cowfiles (ls /usr/local/share/cows) and piping it to the gshuf (on linux this is just shuf) command. gshuf -n 1 takes the list, shuffles it and returns exactly one entry. It might returns something like moose.cow.
You might have noticed that this whole ls | gshuf thing is wrapped in $(). By wrapping commands like this you execute them in a so called subshell. Essentially the command in the parentheses is run, and is substituted by its output. In our case this means that we are actually running something like cowsay -f "moose.cow" <...>. It is generally advised to wrap subshell calls in quotes to avoid argument splitting.

So far we picked a random cow to say something. What we need now is the joke that the cow should tell. I checked and of course someone actually made a site that you can use as an API to get jokes. We use curl to make a web request to the domain https://icanhazdadjoke.com. The website actually notices, that we are not a browser but curl and just returns a string. This looks something like this:

knut@Knuts-MBP ~> curl https://icanhazdadjoke.com/ 2>/dev/null
Why did the girl smear peanut butter on the road? To go with the traffic jam.

Now we just need to use our newfound knowledge and wrap this in another subshell to finally get the joke as the parameter to cowsay. Once the subshells ran, our command would look like this:

cowsay -f "moose.cow" "Why did the girl smear peanut butter on the road? To go with the traffic jam."

The subshells will be newly evaluated with every call from watch so every time we get a new cow and a new joke.

Feel free to try it on your machine and let me know how it is going (and also if you get a particularly good joke 😉).