More Than a Dozen Command Line Tools I've Written—and So Can You!
Way back in 2013, I wrote Google Go: The Good, the Bad, and the Meh, which means I now have more than the job-recruiting-required “at least five years of experience” using Go. But I don’t want to write about that. I want to write a little about a baker’s dozen of the many small tools I’ve written in Go to scratch personal itches since then.
Most of these programs were the result of some passing enthusiasm, now mostly forgotten. They tend to be work-adjacent but not actually part of my direct job responsibilities. (That is to say, I was never asked to write any of these by a boss, and probably my bosses would see them as a waste of time if they knew about them.) Some of them I use a lot, and others I wrote and then forgot about completely. Mostly though, they’re just fun to write and satisfying to look back on.
- Description: Gets a URL and pretty prints the headers it finds.
- Origin: I was worried my server was adding double headers to some responses (it was) and wasn’t GZIPing properly (it wasn’t).
- Status: Pretty much complete. The only thing I can think to add would be a way of passing arbitrary headers to use to it, but at that point, it would become a curl clone.
- Usage: I use this all the time.
- Satisfaction: This is one of my favorite tools. It doesn’t do something I couldn’t do by either using curl or looking at the network inspector in my browser, but it is significantly easier to use and read, and that’s enough to make me keep using it.
- Description: Formats JSON from standard in, file, or URL.
- Origin: I had some ugly JSON and I was tired of Googling for a good online formatter.
- Status: Complete.
- Usage: Up until now, I’ve been using this any time I have to deal with fresh JSON, so that I could get a sense of its structure, but I think I may switch to gron for exploring new data sources in the future.
- Satisfaction: This has repaid the time spent writing it by 100x.
- Description: Decodes and encodes from common formats like Base64, URL query escaping, ROT13, etc.
- Origin: I was sick of launching UnicodeChecker whenever I needed to do some simple URL escaping or something. I think a spoiler blog post written in ROT13 was what pushed me over the edge to write it.
- Status: I still would like to add more encoding/decoding formats.
- Usage: This is pretty new, but I’ve already used a couple of times since I wrote it.
- Satisfaction: I’m happy with this, but I still need a good way to explore Unicode ranges if I’m going to be able to total quit using UnicodeChecker.
- Description: Creates random passwords. It also has a web version.
- Origin: I was working with an Enterprise Organization that required us to create passwords with exactly 8 characters, containing uppercase, lowercase, and digits (no symbols!). And of course we had to replace the passwords every three months.
- Status: I would like to release a companion tool for correct-horse-battery-staple–style passwords, but I don’t have any plans to work on it.
- Usage: I still use it for, e.g., creating MySQL account passwords (but with more than 8 characters and with symbols).
- Description: A timer that defaults to one pomodoro unit with a fancy ASCII art counter.
- Origin: I was in a meeting thinking about how I should start doing pomodoro to be more productive, and then I sank all my time into this instead.
- Status: I wish it could count down to a specific time instead of only for a specified duration, but I never came up with a good way to write out target times on the command line.
- Usage: I used to use this all the time for brewing coffee, but now I only use this to remind myself to jump on calls in 15 minutes. I think I’ve used it to do approximately 6 pomodoro units since I wrote it.
- Satisfaction: I feel like I learned a lot about how writing composable GUI framework is very easy in systems that aren’t HTML+CSS.
- Description: Makes asset hashes for immutable caching of static resources.
- Origin: I was jealous that Django has this as a feature and wanted it for my static site blog.
- Status: I have an open issue that I don’t know when I’ll have time to implement.
- Usage: I not only use it for this blog, I use it at work for a static site there too.
- Satisfaction: I think this could be rewritten at some point.
- Description: Solves sudoku puzzles.
- Origin: I wanted to write a blog post about translating Python to Go.
- Status: I still have a lot of ideas for making it faster. I want to add a mode for generating new puzzles.
- Usage: I used it once to solve a non-test puzzle. I was on an airplane with my laptop, so I opened the laptop, typed in the puzzle and then looked at the solution with a sense of satisfaction.
- Satisfaction: Good but could be faster.
- Description: Recompresses images to make them look like shitpics.
- Origin: I was sick of seeing shitpics on the internet, so I decided to embrace the problem.
- Status: I wish it had a web component, but I’ve never sat down and thought of a good way to do it.
- Usage: I used this a lot when I first wrote it, but now I don’t, I think because it doesn’t have a good web version.
- Satisfaction: I would be happier if I wrote the web version.
- Description: Tells you how large a file will be after GZIP compression and what the ratio is.
- Origin: A coworker wanted to know how large his React app was. (It was large.)
- Status: Done.
- Satisfaction: Meh.
- Description: Prompts you to compare items in a list so you can make an overall ranking.
- Origin: My nerd friends were ranking their favorite Legend of Zelda games, and I wanted to make sure my ranking was really thoroughly considered.
- Status: Weirdly, it took me a long time to get a sorting algorithm that I liked, but I think it’s done now that it uses binary insertion sort.
- Usage: I used it for this blog post (notice that satisfaction is decreasing).
- Satisfaction: It turns out that O(n log n) is a lot more comparisons than I’m willing to do when the list is larger than a handful of items.
- Description: Given a file on standard in, guesses its MIME type according to the WhatWG MIME sniffing standard.
- Origin: I wanted to know what kind of file something was and I forgot the name of the Unix file command. (To be fair, my name is more memorable.)
- Status: Done.
- Usage: I think I have used this twice for real, but the second time I also remembered the name of
- Satisfaction: It’s fine for what it is.
- Description: Reverse proxies from one port to another (typically 8000 to 80).
- Origin: A coworker was having CORS problems due to mismatching ports, and I said it wouldn’t be too hard to make a reverse proxy so his ports matched.
- Status: I think this would be more useful if it let you multiplex different ports onto one, so you could run different apps simultaneously and have them all work.
- Usage: I haven’t used this in forever.
- Satisfaction: I don’t think this is useful enough as written and I haven’t taken the time to figure out a multiplexing format that is simpler than just running Nginx or Caddy.
- Description: An endless honeypot of Markov chain text to waste the time of spambots.
- Origin: I wanted to play with writing an HTTP handler where the data source was unbounded.
- Status: Live on heroku (but I’m not linking to it because you shouldn’t connect to it since it will crash your browser if you leave it open too long).
- Usage: I actually do use this at work. I tell bots looking for wp-login.php to go to Heffalump on Heroku instead. It doesn’t do anything to slow the bots down, but it makes me feel good.
- Satisfaction: I’m sad that this has so many Github stars. This project isn’t actually serious, and no one should use it, but it’s the most popular thing I’ve written.
I’ve actually written more than that, but these are the ones I wanted to write about.
So, what’s the moral of all this? The moral is that it’s fun and easy to write a command line tool in Go. Even if only you use it, and even if there’s already another way to do the basically same thing, you will learn something and have fun writing your own tool. Just dive in, open godoc.org and start writing a thin wrapper around some existing functionality so that it meets your needs and maybe you will learn something along the way.