I love watching the masters at work. Trying to keep up as Rick Harding plows through code in vim, riding along as Sam Jones conducts multiple sessions in tmux, or marveling at Suz Hinton’s setup for live coding inspires me to up my own game and become a more productive developer. With that in mind, a few weeks ago, I asked my fellow Test Double agents to share some of their favorite command-line tools (other than text editors).

My question in Test Double's Slack: I'm curious what are your favorite command line tools? What hidden gems turbocharge your productivity?
Polling the Test Double brain trust

Enablers

We’ll start with two tools that enable all the rest.

Homebrew

Website: https://brew.sh

Installation: /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)

If you’re on macOS, then brew is a great way to install and manage all the rest of the tools listed below:

# Search for a package
brew search hub

# Find out more info about a package, including good post-install steps
brew info postgresql

# Install a package
brew install rg

That’s just scratching the surface of what brew can do. brew cask can install GUI applications, extending brew’s package management into the .app realm:

brew cask install firefox
brew cask install iterm2

Need a particular font for your favorite editor?

brew tap homebrew/cask-fonts
brew search font
brew cask install font-firacode-nerd-font

Homebrew supports bundling packages into a Brewfile. I keep an updated Brewfile with essentials for new laptop setup:

...
brew "git"
brew "vim"
brew "zsh"
...
cask "firefox"
cask "iterm2"
cask "bbedit"
cask "1password"
cask "fantastical"
...

Some tools, like Brooke Kuhlmann’s macOS or ThoughtBot’s Laptop, expand on the Brewfile to create a rich system for quickly spinning up a new Mac. That said, a simple Brewfile (with backups of course) is a great way to get up and running after disaster and/or a new hardware purchase.

tldr

Website: https://tldr.sh/

Installation: brew install tldr

Where was tldr back when I was in college?!? man pages were so detailed and in-depth that finding command-line help was a definite chore. Compare man tar versus its tldr tar counterpart:

Screenshot of running the man tar command
man tar
Screenshot of running the tldr tar command
tldr tar 🌈

I do not remember things like where I put my wallet, so I’m definitely not going to remember how to tar and gzip a directory. Consequently tldr is an indispensable reminder of how to get stuff done.

Daily Stalwarts

Next up are the everyday tools, the workhorses of a typical day writing code.

ripgrep or The Silver Searcher

Website: https://github.com/BurntSushi/ripgrep

Installation: brew install rg

Website: https://github.com/ggreer/the_silver_searcher

Installation: brew install ag

ripgrep and The Silver Searcher (rg and ag respectively on the command-line) were both written as faster replacements for ack, but much, much faster. The Silver Searcher came along first, but ripgrep is slightly faster. Test Double agents are split pretty evenly with one vote also for git grep.

Both tools let you specify a pattern to constrain searches. For example, if you want to find all the TODO comments in your javascript files:

rg 'TODO' -g '*.js'
ag 'TODO' -G '*.js'

These days I use rg, having moved over from ag; here’s why:

  • Faster
  • Ignores everything specified in .gitignore/.ignore/.rcignore files
  • Can search specific file types, e.g., rg 'TODO' -tjs as another way to search Javascript files
  • Full support for Unicode and colors; yes, you can rg 👍
  • Can search gzip, bzip2, and other common zipped file formats with the -z flag

A slight bump in speed, more power, and fewer bugs gives ripgrep the edge for me, but both tools are a big improvement over ack or grep.

hub and/or gh

Website: https://github.com/github/hub

Installation: brew install hub

Website: https://github.com/cli/cli

Installation: brew install github/gh/gh

hub is the unofficial command-line tool for interacting with GitHub, while gh is the still-in-beta eventual replacement. We’ll look at hub first, as it’s still a more full-featured tool.

I use hub for three main tasks:

hub sync
hub pull-request
hub pr checkout

hub browse opens your current branch on GitHub in your default browser. I personally prefer viewing diffs in GitHub’s UI, so this is a nice shortcut to pop from the command line over to the browser.

hub sync is like doing a git pull for all of your local branches. It’s particularly handy when you want the latest on master for a rebase but don’t want to have to mess around with checking out master, doing the git pull, and then checking out your branch again.

hub pull-request lets you create a pull request without having to leave the command line. hub pull-request -d can be used to create draft pull requests.

hub pr checkout lets you checkout a specific pull request. Let’s say you need to run a particular PR, #123, locally as part of reviewing and testing it. hub pr checkout 123 finds the associated remote branch and pulls it down locally.

Now let’s talk about gh. One of the confusing parts of hub is that it has two different subcommands for doing things with pull requests, as highlighted with the hub pull-request and hub pr checkout commands above. gh fixes that, merging all pull request operations under one pr subcommand:

gh pr create is the equivalent of hub pull-request.

gh pr checkout is the equivalent of hub pr checkout.

gh pr view is the equivalent of hub browse, with the extra ability of being able to open any pull request in the browser, not just the current branch.

Whoo-whee! That is one beautifully consistent interface!

As already mentioned, gh is currently beta software, but by the time you read this post (hello, future you!) it may be ready to supplant (or having already supplanted) hub, so it’s worth talking about both.

ngrok

Website: https://ngrok.com

Installation: brew cask install ngrok

Not every project will need ngrok, but when you do, you’ll be using it on a daily basis. ngrok's website describes it as: “One command for an instant, secure URL to your localhost server through any NAT or firewall.” Why would this be useful? Let me count the ways:

  • Enabling your remote coworkers to test or review your locally-running app
  • Webhooks or 3rd party APIs that need to call back to a publicly accessible URL
  • Demoing an app or web site to clients

Let’s say—and this is strictly hypothetical—that you’re writing a blog post. You want to go through one round of proofreading and edits before going to PR. Your local server is running on localhost:1313 but you’d like a publicly-accessible URL to hand to your proofreaders.

ngrok http 1313

Voilà! You now have your own URL, routed to an http service running on port 1313 on your machine.

Setting up a public URL for accessing a locally-running server via ngrok
Public URLs for local services is easy with ngrok

Ngrok can also serve up arbitrary directories, so if you want to test some plain old static HTML but need it running on a server (behind basic auth):

ngrok http -auth="user:password" file:///Users/kyleadams/Code/static-site

Hat tip to Adam Lukens for pointing out this nice bit o’ functionality.

pbcopy or xclip

Installation: Included with most *nix-based OSes

This last set of tools is a bit different—they’re included with the *nix-based operating systems. pbcopy and xclip, on macOS and Linux respectively, let you pipe content into your clipboard. Building on one of our previous examples, here we pipe the results of looking for TODO comments into the clipboard, where it’s ready to be pasted into your todo list/productivity app of choice:

rg 'TODO' -tjs | pbcopy

xclip's syntax is a little more verbose than pbcopy; fortunately Gary Woodfine has a nice set of aliases to bring pbcopy ease-of-use to Linux systems.

fzf

Website: https://github.com/junegunn/fzf

Installation: brew install fzf

fzf is a fuzzy finder. Developers may remember TextMate’s Cmd+T shortcut, which invoked a fuzzy finder for files in your project. Start typing any letters in the file’s path or name and it would jump to the top. In turn that feature inspired GitHub’s ’t’ shortcut to activate its file finder. ctrlp brought this fun to vim and now the latest incarnation is fzf. fzf, in addition to being a command line tool, also works in vim.

Note: If you installed via brew you’ll need to do a little extra work to finish fzf's command line installation:

/usr/local/opt/fzf/install

Restart your shell or source your config file and you’ll be good to go. fzf's installation integrates it into your shell experience; for example, using Ctrl+r to invoke a reverse search through your command history now has fuzzy finding powers.

Using fzf to reverse search through command history
Better history searching with fzf

Invoking fzf by itself triggers a file search, equivalent to doing find . -type f | fzf.

Using fzf to search through files
Better file searching with fzf

Where things really get interesting is when fzf is used as part of a pipeline. Fellow agent Ross Brandes wanted to make it easier to attribute work done while pairing. He created, with assistance from Jason Karns, this alias:

alias coauth='printf "Co-authored-by: %s" "$(git log --pretty=format:"%an <%ae>" -1000 | sort | uniq | fzf)" | pbcopy'

Once the alias is setup, typing coauth will:

  1. Find commit authors’ names and emails for the last 1000 commits in git's log
  2. Sort and filter out any duplicates
  3. Send the list to fzf for fuzzy finding magic
  4. Append the selected name & email to the string “Co-authored-by: "
  5. Pipe that to the clipboard for easy pasting

In action:

Using fzf to find co-authors
Quickly and easily finding co-authors via fzf

If you want to dive into fzf more (and you should), Alexey Samoshkin’s Vim universe. fzf - command line fuzzy finder is a good video to watch.

The Lurkers

These are tools that usually end up aliased to a more plain-vanilla command, so that you often forget you’re benefitting from the extra pizzaz they bring to whatever command that might be.

exa

Website: https://the.exa.website/

Installation: brew install exa

Billed as a “modern replacement for ls”. You could spend a lot of time in dotfiles hacking ls to do some of the things that exa does, but why bother? A simple alias in your shell’s startup file will suffice:

alias ls="exa"

Now all of your directory listings run faster, look better, and are Git-aware. You’re welcome. Feast your eyes on these amazing before and after screenshots:

Screenshot of running the ls command
ls
Screenshot of running the exa command
exa 🌈

bat

Website: https://github.com/sharkdp/bat

Installation: brew install bat

Just as exa is a replacement for ls, bat is a replacement for cat:

alias cat="bat"

bat brings goodies like line numbers, syntax highlighting, git change markers, and automatic paging to the table. More beautiful before and after screenshots:

Screenshot of running the cat command
cat
Screenshot of running the bat command
bat 🌈

Furthermore, it can use those syntax-highlighting powers for awesome when tailing a log file:

tail -f /var/log/wifi.log | bat --paging=never -l log

Need to view an older version of a file in Git but still want syntax highlighting?

git show v1.0.0:REAME.md | bat -l md

bat is also pbcopy/xclip aware, so if you pipe to either it will strip the line numbers and git modification markers out before sending file contents off to the clipboard:

bat TODO.md | pbcopy

grc

Website: https://github.com/garabik/grc

Installation: brew install grc

grc is a slick tool for colorizing log output. It recently came to my attention thanks to a lightning talk given by Daniel Flynn on some of his favorite tools. As the docs for grc say, “for the impatient - try [the] following commands:”

grc netstat
grc ping hostname
grc tail /var/log/syslog
grc ps aux
Running the ping command with and without grc for colorization
Watch grc add & 🌈 to every day commands!

We can also set grc up with custom configurations specific to whatever output format we need to make more readable. grc uses ~/.grc/grc.conf as its primary config file. Each entry in this file has two lines:

  1. a regular expression
  2. the configuration file to use for anything matching the preceding regular expression

For example:

rails
rails.conf

Will apply the configuration from rails.conf to any rails command. If we peek in rails.conf we find a different set of two lines:

regexp=\b\w+::\w+Error\b
colours=bold white on_red

This setup colorizes anything matching the regular expression; in this case, highlighting errors. Running grc rails server and hitting a non-existent route colors the error as nice, bright, white-on-red text:

Screenshot of a highlighted error in a Rails log
Easy-to-spot errors in your log files, courtesy of grc

grc makes the monotonous chore of scanning through long files a tad more colorful and a ton easier.

Mighty Handy

These tools may not be used daily, but they’re incredibly useful when the need arises. Note that tldr can be handy to remind you how to use these less frequent flyers.

httpie or curlie

Website: https://httpie.org/

Installation: brew install httpie

Website: https://curlie.io/

Installation: brew install rs/tap/curlie

HTTPie and curlie are both replacements for the workhorse in a web developer’s command line toolbox, curl. Much as with rg vs ag, which one you use is entirely up to you as both are significantly better than what they’re replacing. It’s important to note that, unlike the lurker tools, they are not drop-in replacements for curl as they have a different interface. HTTPie is a new tool from the ground up, including the new interface, while curlie is a wrapper around curl to give it HTTPie’s easy-on-the-fingers user experience. Less talking, more showing, amiright?

GET

Screenshot of using curl to perform an HTTP GET
curl
Screenshot of using httpie to perform an HTTP GET
HTTPie 🌈
Screenshot of using curlie to perform an HTTP GET
curlie 🌈

PUT of JSON data

Screenshot of using curl to perform an HTTP PUT
curl
Screenshot of using httpie to perform an HTTP PUT
HTTPie 🌈
Screenshot of using curlie to perform an HTTP PUT
curlie 🌈

I personally prefer HTTPie because I think its output is a little easier on the eyes, but I have no beef with others who prefer curlie's speed advantage.

jq

Website: https://stedolan.github.io/jq/

Installation: brew install jq

At its simplest, jq is a pretty printer for JSON data. At its most powerful, jq lets you slice, dice, and query JSON data from the command line. To demonstrate the power of jq, let’s start with a simple curl command (note that you can sub in http or curlie if you’ve alread installed either of those tools) that will search books via the Open Library API:

curl http://openlibrary.org/search.json?q=the+lord+of+the+rings
Curling complex JSON data without jq
Curling without jq

That’s quite a few books for a humble hobbit. Fortunately jq lets you drill down into this data. If you just want JSON pretty printing and syntax highlighting, you can tell it to return the root node, i.e., everything:

curl http://openlibrary.org/search.json?q=the+lord+of+the+rings | jq '.'
Curling complex JSON data and using jq as a pretty printer
Using jq as a pretty printer

Want the first book only?

curl http://openlibrary.org/search.json?q=the+lord+of+the+rings | jq '.docs[0]'
Curling complex JSON data and using jq to grab the first item in a list
A simple jq query

Better, but still too much information. What if we only want the title and author of the first book? jq uses the | operator to send the results of one query along to the next:

curl http://openlibrary.org/search.json?q=the+lord+of+the+rings | jq '.docs[0] | {title: .title, author: .author_name}'
Curling complex JSON data and using jq to grab the specific fields from the first item in a list
Stringing together multiple jq queries

Much better. jq's powers extend far beyond our simple example, so delve into the manual or this very nice tutorial if you wish, but remember: don’t dig too far or you may wake the drums in the deep!

tig

Website: https://jonas.github.io/tig/

Installation: brew install tig

tig is a command line visual interface for git; where it really excels is in browsing a repo’s history.

Using tig to browser a repo's history
Browsing a repo's history with tig
Using tig to view the diff on a particular commit
Viewing the details of a specific commit in tig

One nice use/trick is to use tig to browse through and clean out your stash:

tig stash

lsof

Website: https://people.freebsd.org/~abe/

Installation: Included with most *nix-based OSes

Stop me if you’ve been here, done that: you start up your dev server, only to get a message that the port is already in use. You double check and confirm that you don’t have another instance running in a different open session. How do you find out which process is holding onto that port? lsof stands for LiSt Open Files and provides a way to see which processes are opening which files. Since a port is handled as a file in *nix, we can use it to find which process is holding onto a particular port (8080 in this example):

lsof -i :8080

This is one of those tools that I only need every once in awhile, just long enough for me to forget how to use it… which is where tldr comes in handy.

xargs

Installation: Included with most *nix-based OSes

xargs is a *nix tool that takes input, splits that input up into a list, and then runs a provided command against every item in the list. It’s also one of those things that’s terribly abstract in explanation but so much cooler in practice. Fortunately developer Wes Bos provided a wonderful example of using xargs in a recent tweet:

🔥 Open all files that have a merge conflict in your editor

git diff –name-only | uniq | xargs code

Add an alias to your ~/.zshrc or ~/.gitconfig

xargs is a unix thing, Windows users use WSL

alias fix="git diff --name-only | uniq | xargs code"

Let’s break that down a bit.

  1. git diff --name-only provides a list of names for files with difference in them
  2. uniq filters that list down to unique file names
  3. The list of unique file names is handed to xargs along with the command code, which invokes Wes’ editor of choice

The end result is that each file with a merge conflict opens in the editor, ready for resolution. Note that you could easily swap code out for vim, emacs, atom, or whatever you prefer.

Bonus Content

Congrats on making it this far (or cheating and jumping right to the end)! If you’ve persevered, you deserve a treat, so our final tool is something its author describes as “simple and silly”.

cowsay

Website: https://github.com/tnalpgge/rank-amateur-cowsay

Installation: brew install cowsay

Without further ado, I present cowsay (also courtesy of Daniel Flynn):

$ cowsay 'What do cows do for entertainment?'
 ____________________________________
< What do cows do for entertainment? >
 ------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

$ cowsay 'Go to the MOOOO-vies!'
 _______________________
< Go to the MOOOO-vies! >
 -----------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

What Next?

There are a lot of tools, maybe even an overwhelming amount, listed above. Choose one that seems most interesting to you and then incorporate it into your daily workflow. Master it. Once you’re comfortable, come back and choose a new one to learn. My hope is that you leave this blog post feeling inspired by one or two of the examples above, ready to take the next step in boosting your productivity as a developer.

A shiba inu dog typing… OR you after leveling up your command productivity with these tools

Kyle Adams

Hash An icon of a hash sign Code Name
Agent 0033
Location An icon of a map marker Location
Indianapolis, IN