Zephyr On Call

Hello and welcome to Zephyr On Call. My name is Andrew Quinn.

Pardon the spartan look, as I am redesigning my blog’s CSS from scratch, using my minimum-viable-hugo repository on GitHub, as well as figuring out a blogging strategy that works for me. I hope you take something from here regardless.

So you've installed `fzf`. Now what?


Software engineers are, if not unique, then darn near unique in the ease with which we can create tools to improve our own professional lives; this however can come at a steep cost over time for people who constantly flit back and forth between different tools without investing the time to learn their own kit in depth. As someone with a healthy respect for the tacit knowledge of people better than me, I think a great 80/20 heuristic is “Learn the oldies first”: venerable Unix tools like cat, ls, cd, grep, and cut. (sed and awk, too, if you have the good fortune of landing yourself in an actual modern sysadmin role.)

But there are tools whose return on investment is so immediate, and whose value prop is so unique, that the 80/20 heuristic breaks down entirely for them. fzf is one of them. And it makes me sad to see so many people download it, run it as-is at the command line, and then just shake their heads and walk away, saying “I don’t get it”.

Here I want to change that. Pretend you live on a more-or-less standard Ubuntu box. You’ve just installed fzf – now what?

First try Ctrl+R

In most terminals, Linux and Windows alike, Ctrl+R gives you backwards search for your commands. The reason you, like me, may not have heard about this until you had already been hacking away for ten flippin' years at the shell is because the base version kind of sucks for 2 reasons:

fzf is a bit of a weird program because installing it actually overwrites a whole bunch of keyboard shortcuts, in the interest of making them better. Normally I would hate this. But…

… This is a considerable improvement on the baseline.

… now try Alt+C

Let’s say you boot into an empty terminal. You’re trying to quickly find your nascent SaaS side hustle repos and cd to it - but it’s been weeks since you’ve been there, your actual full time job has been unusually fun and engaging… How do you find it?

Answer: With fzf. fzf rewrites Alt+C into a souped-up fuzzy-cd shortcut that lets you hop around very quickly when all you remember is the vague name of the directory in question.

The base fzf command

Okay, we’ve got the shortcuts out of the way. Honestly these two guys alone provide the majority of the value I get out of fzf - but let’s look at what the command, by itself, does.

It fuzzy-finds file locations! Relative ones, at least, to your own directory. This… isn’t that useful, by itself.

But try vi $(fzf), and…!!!

And you get a fuzzy-open-in-editor experience!

(There’s nothing special about vi in this regard, btw. You can call it with emacs, nano, code, whatever floats your boat!)

vi $(find . '/' | fzf): For finding random config files

The other day I was trying to hack together baby’s first live-reload with a Firefox extension, entr, and nginx. And I found myself asking: Where the heck is nginx.conf?

I reviewed my options. I could

  1. Use my half-remembered knowledge of the FHS to guess around, with trees and greps, or
  2. Just know and commit it to memory and feel superior to everyone else, or
  3. Just pipe find . ‘/’ to fzf and start searching.

I like this clip a lot because it shows some of the subtle tradeoffs of using fzf, as well as one of the more advanced searching features - searching for conf$ will filter out any line that doesn’t end in conf. Notice that fzf temporarily wigs out when find hits a whole lot of “Permission denied” errors - but then recovers itself a few seconds later.

Are those extra few seconds worth the tradeoff for being able to find config files in such a braindead manner? It is for me.

Introducing rg: Fast, recursive-by-default grep

Everything I say below can be done with grep as well, but the recursive-by-default nature of rg (also known as ripgrep) is where the tool really comes into its own. I highly recommend you download it and use it for the following examples as well. But if you’re toolshy, don’t worry!

rg . | fzf: Fuzzy search every line in every file

rg . | fzf | cut -d ":" -f 1: Fuzzy search every line, in every file, and return the file location

vim $(rg . | fzf | cut -d ":" -f 1): Fuzzy search every line, in every file, and open that file

Your choice of challenges


Hello from sunny Kalajoki! My wife and I are in this little agrarian town because she is retaking a few of her Finnish high school exams, which requires her to go back to where she grew up to do so. She’s retaking her exams in English and math, after a ~6 month Anki-based training regimen we put her through. I think she’ll do extremely well, and I’m immensely proud of her.

As for moi, I have the good fortune of a very understanding boss, who allowed me to work remotely for this week. (I already get 3/5 days remote, but one of the exams was scheduled on one of our in-office days - one of the many perks of working in tech.) And so now I’m looking out the window of our Airbnb, at the clear blue sky and the yellowing sunlight and the green spruce trees and the blood red branches of a bush I remember walking past so many times when we used to live here, and thinking about challenges.

The world offers no shortage of challenges to all of us. The variety of what we’re allowed to take on is bewildering, beyond what nature was capable of providing for a very long time. When you look at the natural ecosystem what you see is an inherently unstable, but learning system - one which slowly converges in on hard dependence on the few things which stay constant. Old growth forests come to depend on the few trees which have lived for thousands of years there, content to assume they will live for thousands more; this stability in turn depends upon the sun waking up and circumambulating our globe once a day, upon the soil staying clean and detoxified. All the critters of the forest, with their exponentially more ephemeral lives, learn to accept these as constants – or as good as constant for ones with such short lives.

Humans turn this into an anomaly, by virtue of our long lives and will to power. Our intellects have allowed us to slowly but surely reconfigure everything in our environment to the point where we can truly go anywhere, and take anything on, and it all feels meaningless to the great mass of people who think this means it is equally okay to take nothing on. How strange!

After taking on the challenge of getting an elite education in the West, I decided to take on a quite different challenge - that of relocating myself to Northern Europe, learning to brave the cold, learning to speak the strange Finnish language. Sometimes I wonder to myself why I took all this on in the first place - I could surely make more money elsewhere, right?

But these challenges are the ones I chose. At some level, that has to trade off against financial concerns.

Getting my hands dirty with C#


I know that GPT-4 is all the rage right now, but I decided to spend some time this weekend on something a little less sexy: learning to work with Microsoft’s original attempt at a Java killer, C#. Some quick projects:

“Really, C#? Why? You already know like, Python and Haskell and a million random command-line tools. What’s the appeal?” Money, baby. Outside of the Valley everyone and their uncle is still using either Java or C#, and I had enough Java in AP Computer Science twelve years ago. Besides, working my way through Haskell Programming from First Principles last year while I was unemployed was something I did for me, not for the market; I’m a firm believer in listening to the market when it’s giving you hints.

I’m also a firm believer in the wisdom of the traveling salesman. Your first programming language might be an arbitrary choice, but your second one almost certainly won’t be, nor will the first database you work with, the first third-party library import, etc. How do you choose what to learn or do next? Turns out you can get pretty far just learning the most obvious, popular thing.

In my case, the “most obvious, popular thing” is C#. I already know PowerShell better than I have any reason to, and C# ties in to PowerShell beautifully. The backend developers at my current job all use C# - and a lot of them are veterans, having progressed to C# from the halcyon days of Finnish ’90s and ’00s C++. These guys know their stuff! And, finally, I’ve already been immersed in the Microsoft and Azure twin ecosystem for months now – SQL Server is in my bloodstream right now, as are all the most common assets created on that cloud platform. C# works with all of those technologies impressively well.

Here’s what my setup looks like so far:

Automating Windows Updates with PowerShell and Octopus


It’s a quiet Saturday morning here in lovely Tampere, and I’m watching the update progress on an ancient bit of fintech from Windows COM programming days. It’s as simple as it gets, operationally speaking. There’s a handful of Windows server VMs; a few SQL Server machines, a few more for the business logic, and a few for the frontend. An SFTP server for flavor - don’t sleep on SFTP just because REST APIs are all the rage nowadays.

‘Bag of VMs’ of course means ‘mandatory OS patching’, which is what I’m up to today. One of Microsoft’s many sins is not including a command line OS patcher, so schlubs like me have to resort to a third party PowerShell package called PSWindowsUpdate to automate the job.

Getting this baddie on these Internet-shy machines Internet is a story for another day. Once I did get it on, I got to use a tool which is much better than I was expecting it to be: Octopus Deploy, a system orignally designed for CI/CD stuff but which happens to serve admirably as a remote script executor in a pinch. Since I inherited this architecture with Octopus already in place anyway, I just stuck with this instead of installing something sane like Ansible.

The setup is simple:

  1. You install Octopus on some oct-vm.
  2. You install Octopus tentacles on all machines you wish to deploy to (or just run scripts on).
  3. Unlike Ansible, you get to use a quite nice and readable web front-end to customize the steps you want your scripts to run in, which machines you want them to run on, whether it should be run on all machines at once or one at a time round-robin style, …
  4. As your scripts run remotely, any Write-Debugs you included get streamed to the front-end as well, letting you watch in real time as your scripts whir elsewhere.

There’s a lot of flexibility in the Octopus web frontend. And what I’m a particular fan of is just how discoverable all of this flexibility feels – that’s something you don’t get out of the box with ordinary CLIs, even if their composability is exactly what I’d reach for in past a certain bar of complexity. (I have a coworker who is a big fan of this new wave of halfway-house TUIs that is mostly getting baked up by the Rust community, which shows some promise towards bringing GUI-level composability direct to the terminal. I’m not fully sold yet - but I do have to admit fzf’s minimalistic TUI has fast become a mainstay in my workflows. So maybe there’s a there there, I don’t know.)


I later realized PSWindowsUpdate was probably hanging due to all of the logged-in but disconnected users. So I added a step using quser, msg, and some awk-flavored splitting to kick everyone off the server with a short 5 minute leadup. This SO answer contained a script which I used almost unaltered, save for a

msg $session.Username 'Updates are about to begin. You will
be kicked in 60 seconds.'
Start-Sleep 60

above the logoff.