As a programmer, my computer use can sometimes look anachronistic, what with my heavy use of Emacs and my use of the terminal to get my work done. I spend an inordinate amount of time looking at monospaced text, and I get a lot of my work done inside my shell of choice, zsh(1), although admittedly, most of my experience with the shell has been in bash(1), and so I’m more familiar with a lot of bash-isms, especially when it comes to the affordances the shell gives you for writing shell scripts — a lot of things are transferable between the two, nevertheless, so I found it quite nice to switch, and there’s a bunch of things that make zsh(1) a slightly more pleasant environment to use interactively.

I really do think that knowing how to use the shell is a Must Have skill for any developer, even if you don’t spend your time in the terminal like I do. It doesn’t mean you have to write shell scripts per se; it can be as simple as figuring out how to get the most out of your shell of choice, knowing how to construct pipelines, or exploiting various features of modern-day shells. Here’s a few of the stuff that make my life on the command line pleasant and productive.

Creature Comforts

My use of the command line can be anachronistic, but that doesn’t mean I have to work like it’s the 1970s through a slow teletype and a 2400 baud modem. No, this is 2018 dammit, and that means using a bunch of features that your terminal emulator and shell gives you, including color, line editing, history, and tab completion— things that you need today to get the most out of your shell experience.

But! The first deal is a choice of typeface and a choice of screen colors. You’re going to stare at that screen for hours; better choose a typeface that’s both readable and looks good. My current dev machine is does not have a Retina screen, so that also means the typeface has to have good contrast and letter shapes at low pixel densities.

For a while, I used one of the preinstalled fixed-width typefaces on macOS (née OS X), Monaco, which was nice, but had some issues at the small sizes I kept using it in. I experimented with turning off antialiasing at the size I used (10pt), which yielded fairly nice sharp text, but I felt wrong turning off antialiasing at all, even though at the pixel density of my screen it did help a lot with readability. It just felt ugly to me, which is definitely not an objective assessment to be fair: I just wanted it to look good as well.

I tried out various other typefaces, including Adobe’s Source Code Pro and Inconsolata, but I finally settled on Hack, for mostly subjective reasons. I like how Hack’s glyphs look narrower to me than the others, and I also liked how code looks in it. Inconsolata looked too muddy at the size I wanted, while Source Code Pro’s glyphs looked too wide and squat for my taste. Another issue was that I wanted the same typeface on both Emacs and iTerm2; Emacs had a weird time handling Source Code Pro at the size I used. So, Hack it is.

In terms of colors, I like to use a dark background, instead of blasting my eyes with all that light, especially at night. Admittedly, my choice of colors is terrible in bright sunlight: I sometimes have difficulty reading my screen under the glare of sunlight, but that’s a situation I’m rarely in, so it’s worth the tradeoff. I have no idea where my current colors come from, to be honest, as I’ve tweaked it over the years. I think it was originally a preset with white text on a dark blue background, which then became a dark grey background, similar to Slate or what not, and which then turned back into a darker blue background. I probably adopted the color scheme from my xterm(1) and Gnome Terminal presets way back. If you want to check it out, you can download it here.

Aside from the eye candy (which, if I’m honest, it is), I also prefer to keep a terminal window invokable via a hotkey, a habit I picked up years ago on Linux when I used ion3 as my window manager of choice. That terminal serves as my go-to place to Get Things Done, if it’s something that isn’t particularly related to a project. I like to keep the terminal output consistent, so that only things related to a project (such as build results, or a log tail) are in a particular terminal. Everything else goes to my hotkey window.

I also tend to use tmux(1) for terminal multiplexing, particularly on servers I manage. I like how iTerm2 integrates pretty well with tmux(1), particularly in how iTerm2 can represent tmux(1) windows as native iTerm2 windows and tabs. Since it can get confusing, I have a separate color scheme for attached tmux(1) windows. Previously, I was a heavy screen(1) user, and I still am very familiar with its keybindings — I will occasionally use it, if tmux(1) isn’t available, or if iTerm2 doesn’t support integrating with the particular version of tmux(1): I haven’t learned some of the keybindings, so I do get somewhat lost if iTerm2 doesn’t integrate with it.

Shell Config

There’s a bunch of ways to get zsh(1) configured the way you want. In particular, there are several zsh(1) configuration/module systems; I’ve chosen to use zplug, which was suggested to me by Zak on Slack when I asked for suggestions. I’ve been pretty happy with it so far, as there isn’t too much magic in the config, and my .zshrc reads in a fairly straightforward way. I’ll probably post my dotfiles one of these days, when I get around to it.

Getting Around

Although I spend a lot of time in the shell, I have to admit that I don’t write a lot of shell scripts; a lot of my shell use has been ephemeral, and most of the time I only need to do something once or twice. For those times I need to execute something repeatedly, an alias usually suffices.

However, I do have a function installed in my .zshrc for prj, which allows me to cd to a project directory, with shell completion: in my home directory, I always have a ~/projects directory, which contains the code of all my projects, organized the way I like it. Under ~/projects, I have one directory per category of projects: work, personal, skunk works, etc.; each project would then be under one of those categories.

It’s pretty straightforward to just cd to a particular project directory with Tab completion, but I’m often somewhere in the disk hierarchy, so I have that particular shell function for going around. The function itself isn’t anything too spectacular— I just use the CDPATH environment variable to nudge cd to jump to a directory under the ~/projects hierarchy. The way CDPATH works with cd is it provides an alternative list of paths that cd will use to find the directory to change to, instead of just the current working directory.

I’ve been using that shell function for quite a while now, and you can take a look at this gist to get an idea of how I use it; that gist is from my bash(1) config way back when: the equivalent zsh(1) function is actually shorter, but similar, and most of the differences are due to the differences in completion support.

Most of the time, as I said, I don’t have shell scripts or functions that I write once and reuse; I instead tend to rely heavily on shell history to get around. C-r (^R, or Control-R) is my friend, and often I have a good memory of commands or pipelines I’ve executed before. If all else fails, I can always do history 0 | grep $foo or history 0 | less to search for something in my command history manually — I have zsh(1) configured to keep history, with the defaults from the history module from prezto.

The other thing I find myself using quite heavily to navigate are shell globs: those things that allow you to specify files whose name matches particular patterns. Most people are familiar with the * and ? globs, but I learned (only a couple years ago, I might add) the use of the {} glob: it allows you to specify a set of substitutions. For instance, I sometimes have a need to make a temporary backup copy of a particular file:

  $ cp UserService.java{,.bak}

which copies UserService.java to UserService.java.bak. The glob {foo,bar} expands to foo bar; so foo{,.bak} expands to foo foo.bak.

I find it useful especially when I’m on connected via ssh using my phone, as it’s often hard to use Tab completion in such a cramped space, and in those situations I find it easier to just type in what I intend to do anyway.

Pipelines Etcetera

There are a few commands I use pretty heavily in my day-to-day shell use. One of the go-to commands I use especially for pipelines is xargs(1), as it allows me to stitch together the arguments to another command from the output of a different shell command. For instance, one of my most recent use of xargs(1) is to count the lines of all of the checked-in code inside the git repository of my current project:

  $ git ls-files | xargs wc -l

My current project uses git submodules, so I often find myself needing to look for all references of a string, particularly within Java source files:

  $ git ls-files --recurse-submodules -- '*.java' |
    xargs wc -l

I also find myself using pipelines in general for a bunch of stuff, and not just with xargs. Pipelines are the thing to learn when in the shell, if you ask me, as it allows you to stitch things together.

Another command I exercise a lot is less(1): it’s been my default pager since the first time I used a Linux machine. I often use it instead of tail -f to tail through a log file: hitting F (Shift-F) in less(1) tells it to follow the file, similar to tail(1)s -f switch. Using less in this mode has the added advantage of you being able to interrupt following the end of the file you’re looking at to search backwards for something that’s just scrolled by, or to simply scroll backwards: you’re already in the pager, so it’s pretty convenient.

Finally, grep(1) is always my go-to for grovelling through files to find that one string somewhere; YMMV though, as you might want to check out the alternatives to grep(1), such as The Silver Searcher or Ack— I use grep(1) purely out of inertia, but that doesn’t mean you should too. I also occasionally use sed(1) to do some global string replacements, so grep(1) feels more natural for me to use.

Anyway, I hope you find some of the things I do on the shell quite useful. I don’t claim to be an expert though, and there’s a crapton of things I think I can still learn to do. If you have any suggestions, send me an email or tweet: I’d love to hear some of the things you do to make your shell work for you.

Previously: Workman's Tools